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 while matches!(self.current(), Some(IDENT) | Some(COLON)) {
222 self.bump();
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
552fn is_special_package_name(name: &str) -> bool {
557 if name.starts_with("${") && name.ends_with('}') {
559 return true;
560 }
561 if name.starts_with('@') && name.ends_with('@') {
563 return true;
564 }
565 false
566}
567
568pub trait SortingOrder {
570 fn lt(&self, name1: &str, name2: &str) -> bool;
574
575 fn ignore(&self, name: &str) -> bool;
577}
578
579#[derive(Debug, Clone, Copy, Default)]
581pub struct DefaultSortingOrder;
582
583impl SortingOrder for DefaultSortingOrder {
584 fn lt(&self, name1: &str, name2: &str) -> bool {
585 let special1 = is_special_package_name(name1);
586 let special2 = is_special_package_name(name2);
587
588 if special1 && !special2 {
590 return false;
591 }
592 if !special1 && special2 {
593 return true;
594 }
595 if special1 && special2 {
596 return false;
598 }
599
600 name1 < name2
602 }
603
604 fn ignore(&self, name: &str) -> bool {
605 is_special_package_name(name)
606 }
607}
608
609#[derive(Debug, Clone, Copy, Default)]
619pub struct WrapAndSortOrder;
620
621impl WrapAndSortOrder {
622 const BUILD_SYSTEMS: &'static [&'static str] = &[
624 "cdbs",
625 "debhelper-compat",
626 "debhelper",
627 "debputy",
628 "dpkg-build-api",
629 "dpkg-dev",
630 ];
631
632 fn get_sort_key<'a>(&self, name: &'a str) -> (i32, &'a str) {
633 if Self::BUILD_SYSTEMS.contains(&name) || name.starts_with("dh-") {
635 return (-1, name);
636 }
637
638 if name
640 .chars()
641 .next()
642 .is_some_and(|c| c.is_ascii_lowercase() || c.is_ascii_digit())
643 {
644 return (0, name);
645 }
646
647 (1, name)
649 }
650}
651
652impl SortingOrder for WrapAndSortOrder {
653 fn lt(&self, name1: &str, name2: &str) -> bool {
654 self.get_sort_key(name1) < self.get_sort_key(name2)
655 }
656
657 fn ignore(&self, _name: &str) -> bool {
658 false
660 }
661}
662
663impl Relations {
664 pub fn new() -> Self {
666 Self::from(vec![])
667 }
668
669 #[must_use]
671 pub fn wrap_and_sort(self) -> Self {
672 let mut entries = self
673 .entries()
674 .map(|e| e.wrap_and_sort())
675 .collect::<Vec<_>>();
676 entries.sort();
677 Self::from(entries)
679 }
680
681 pub fn entries(&self) -> impl Iterator<Item = Entry> + '_ {
683 self.0.children().filter_map(Entry::cast)
684 }
685
686 pub fn iter(&self) -> impl Iterator<Item = Entry> + '_ {
688 self.entries()
689 }
690
691 pub fn get_entry(&self, idx: usize) -> Option<Entry> {
693 self.entries().nth(idx)
694 }
695
696 pub fn remove_entry(&mut self, idx: usize) -> Entry {
698 let mut entry = self.get_entry(idx).unwrap();
699 entry.remove();
700 entry
701 }
702
703 fn collect_whitespace(start: Option<NodeOrToken<SyntaxNode, SyntaxToken>>) -> String {
705 let mut pattern = String::new();
706 let mut current = start;
707 while let Some(token) = current {
708 if matches!(token.kind(), WHITESPACE | NEWLINE) {
709 if let NodeOrToken::Token(t) = &token {
710 pattern.push_str(t.text());
711 }
712 current = token.next_sibling_or_token();
713 } else {
714 break;
715 }
716 }
717 pattern
718 }
719
720 fn to_green(node: &NodeOrToken<SyntaxNode, SyntaxToken>) -> NodeOrToken<GreenNode, GreenToken> {
722 match node {
723 NodeOrToken::Node(n) => NodeOrToken::Node(n.green().into()),
724 NodeOrToken::Token(t) => NodeOrToken::Token(t.green().to_owned()),
725 }
726 }
727
728 fn is_whitespace_token(token: &GreenToken) -> bool {
730 token.kind() == rowan::SyntaxKind(WHITESPACE as u16)
731 || token.kind() == rowan::SyntaxKind(NEWLINE as u16)
732 }
733
734 fn strip_trailing_ws_from_children(
736 mut children: Vec<NodeOrToken<GreenNode, GreenToken>>,
737 ) -> Vec<NodeOrToken<GreenNode, GreenToken>> {
738 while let Some(last) = children.last() {
739 if let NodeOrToken::Token(t) = last {
740 if Self::is_whitespace_token(t) {
741 children.pop();
742 } else {
743 break;
744 }
745 } else {
746 break;
747 }
748 }
749 children
750 }
751
752 fn strip_relation_trailing_ws(relation: &SyntaxNode) -> GreenNode {
754 let children: Vec<_> = relation
755 .children_with_tokens()
756 .map(|c| Self::to_green(&c))
757 .collect();
758 let stripped = Self::strip_trailing_ws_from_children(children);
759 GreenNode::new(relation.kind().into(), stripped)
760 }
761
762 fn build_odd_syntax_nodes(
764 before_ws: &str,
765 after_ws: &str,
766 ) -> Vec<NodeOrToken<GreenNode, GreenToken>> {
767 [
768 (!before_ws.is_empty())
769 .then(|| NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), before_ws))),
770 Some(NodeOrToken::Token(GreenToken::new(COMMA.into(), ","))),
771 (!after_ws.is_empty())
772 .then(|| NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), after_ws))),
773 ]
774 .into_iter()
775 .flatten()
776 .collect()
777 }
778
779 fn detect_odd_syntax(&self) -> Option<(String, String)> {
781 for entry_node in self.entries() {
782 let mut node = entry_node.0.next_sibling_or_token()?;
783
784 let mut before = String::new();
786 while matches!(node.kind(), WHITESPACE | NEWLINE) {
787 if let NodeOrToken::Token(t) = &node {
788 before.push_str(t.text());
789 }
790 node = node.next_sibling_or_token()?;
791 }
792
793 if node.kind() == COMMA && !before.is_empty() {
795 let after = Self::collect_whitespace(node.next_sibling_or_token());
796 return Some((before, after));
797 }
798 }
799 None
800 }
801
802 fn detect_whitespace_pattern(&self, default: &str) -> String {
810 use std::collections::HashMap;
811
812 let entries: Vec<_> = self.entries().collect();
813 let num_entries = entries.len();
814
815 if num_entries == 0 {
816 if self.substvars().next().is_some() {
818 return default.to_string();
820 }
821 return String::from(""); }
823
824 if num_entries == 1 {
825 if let Some(node) = entries[0].0.next_sibling_or_token() {
827 if node.kind() == COMMA {
828 let pattern = Self::collect_whitespace(node.next_sibling_or_token());
829 if !pattern.is_empty() {
830 return pattern;
831 }
832 }
833 }
834 return default.to_string(); }
836
837 let mut whitespace_counts: HashMap<String, usize> = HashMap::new();
839
840 for (i, entry) in entries.iter().enumerate() {
841 if i == num_entries - 1 {
842 break; }
844
845 if let Some(mut node) = entry.0.next_sibling_or_token() {
847 while matches!(node.kind(), WHITESPACE | NEWLINE) {
849 if let Some(next) = node.next_sibling_or_token() {
850 node = next;
851 } else {
852 break;
853 }
854 }
855
856 if node.kind() == COMMA {
858 let pattern = Self::collect_whitespace(node.next_sibling_or_token());
859 if !pattern.is_empty() {
860 *whitespace_counts.entry(pattern).or_insert(0) += 1;
861 }
862 }
863 }
864 }
865
866 if whitespace_counts.len() == 1 {
868 if let Some((ws, _)) = whitespace_counts.iter().next() {
869 return ws.clone();
870 }
871 }
872
873 if let Some((ws, _)) = whitespace_counts.iter().max_by_key(|(_, count)| *count) {
875 return ws.clone();
876 }
877
878 default.to_string()
880 }
881
882 pub fn insert_with_separator(&mut self, idx: usize, entry: Entry, default_sep: Option<&str>) {
889 let is_empty = self.entries().next().is_none();
890 let whitespace = self.detect_whitespace_pattern(default_sep.unwrap_or(" "));
891
892 self.strip_trailing_whitespace();
894
895 let odd_syntax = self.detect_odd_syntax();
897
898 let (position, new_children) = if let Some(current_entry) = self.entries().nth(idx) {
899 let to_insert = if idx == 0 && is_empty {
900 vec![entry.0.green().into()]
901 } else if let Some((before_ws, after_ws)) = &odd_syntax {
902 let mut nodes = vec![entry.0.green().into()];
903 nodes.extend(Self::build_odd_syntax_nodes(before_ws, after_ws));
904 nodes
905 } else {
906 vec![
907 entry.0.green().into(),
908 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
909 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), whitespace.as_str())),
910 ]
911 };
912
913 (current_entry.0.index(), to_insert)
914 } else {
915 let child_count = self.0.children_with_tokens().count();
916 let to_insert = if idx == 0 {
917 vec![entry.0.green().into()]
918 } else if let Some((before_ws, after_ws)) = &odd_syntax {
919 let mut nodes = Self::build_odd_syntax_nodes(before_ws, after_ws);
920 nodes.push(entry.0.green().into());
921 nodes
922 } else {
923 vec![
924 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
925 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), whitespace.as_str())),
926 entry.0.green().into(),
927 ]
928 };
929
930 (child_count, to_insert)
931 };
932 self.0 = SyntaxNode::new_root_mut(
934 self.0.replace_with(
935 self.0
936 .green()
937 .splice_children(position..position, new_children),
938 ),
939 );
940 }
941
942 pub fn insert(&mut self, idx: usize, entry: Entry) {
944 self.insert_with_separator(idx, entry, None);
945 }
946
947 fn strip_entry_trailing_ws(entry: &SyntaxNode) -> GreenNode {
949 let mut children: Vec<_> = entry
950 .children_with_tokens()
951 .map(|c| Self::to_green(&c))
952 .collect();
953
954 if let Some(NodeOrToken::Node(last)) = children.last() {
956 if last.kind() == rowan::SyntaxKind(RELATION as u16) {
957 let relation_node = entry.children().last().unwrap();
959 children.pop();
960 children.push(NodeOrToken::Node(Self::strip_relation_trailing_ws(
961 &relation_node,
962 )));
963 }
964 }
965
966 let stripped = Self::strip_trailing_ws_from_children(children);
968 GreenNode::new(ENTRY.into(), stripped)
969 }
970
971 fn strip_trailing_whitespace(&mut self) {
972 let mut children: Vec<_> = self
973 .0
974 .children_with_tokens()
975 .map(|c| Self::to_green(&c))
976 .collect();
977
978 if let Some(NodeOrToken::Node(last)) = children.last() {
980 if last.kind() == rowan::SyntaxKind(ENTRY as u16) {
981 let last_entry = self.0.children().last().unwrap();
982 children.pop();
983 children.push(NodeOrToken::Node(Self::strip_entry_trailing_ws(
984 &last_entry,
985 )));
986 }
987 }
988
989 let stripped = Self::strip_trailing_ws_from_children(children);
991
992 let nc = self.0.children_with_tokens().count();
993 self.0 = SyntaxNode::new_root_mut(
994 self.0
995 .replace_with(self.0.green().splice_children(0..nc, stripped)),
996 );
997 }
998
999 pub fn replace(&mut self, idx: usize, entry: Entry) {
1001 let current_entry = self.get_entry(idx).unwrap();
1002 self.0.splice_children(
1003 current_entry.0.index()..current_entry.0.index() + 1,
1004 vec![entry.0.into()],
1005 );
1006 }
1007
1008 pub fn push(&mut self, entry: Entry) {
1010 let pos = self.entries().count();
1011 self.insert(pos, entry);
1012 }
1013
1014 pub fn substvars(&self) -> impl Iterator<Item = String> + '_ {
1016 self.0
1017 .children()
1018 .filter_map(Substvar::cast)
1019 .map(|s| s.to_string())
1020 }
1021
1022 pub fn parse_relaxed(s: &str, allow_substvar: bool) -> (Relations, Vec<String>) {
1024 let parse = parse(s, allow_substvar);
1025 (parse.root_mut(), parse.errors)
1026 }
1027
1028 pub fn satisfied_by(&self, package_version: impl crate::VersionLookup + Copy) -> bool {
1030 self.entries().all(|e| e.satisfied_by(package_version))
1031 }
1032
1033 pub fn is_empty(&self) -> bool {
1035 self.entries().count() == 0
1036 }
1037
1038 pub fn len(&self) -> usize {
1040 self.entries().count()
1041 }
1042
1043 pub fn ensure_minimum_version(&mut self, package: &str, minimum_version: &Version) {
1066 let mut found = false;
1067 let mut obsolete_indices = vec![];
1068 let mut update_idx = None;
1069
1070 let entries: Vec<_> = self.entries().collect();
1071 for (idx, entry) in entries.iter().enumerate() {
1072 let relations: Vec<_> = entry.relations().collect();
1073
1074 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1076 if names.len() > 1 && names.contains(&package.to_string()) {
1077 let is_obsolete = relations.iter().any(|r| {
1079 if r.name() != package {
1080 return false;
1081 }
1082 if let Some((vc, ver)) = r.version() {
1083 matches!(vc, VersionConstraint::GreaterThan if &ver < minimum_version)
1084 || matches!(vc, VersionConstraint::GreaterThanEqual if &ver <= minimum_version)
1085 } else {
1086 false
1087 }
1088 });
1089 if is_obsolete {
1090 obsolete_indices.push(idx);
1091 }
1092 continue;
1093 }
1094
1095 if names.len() == 1 && names[0] == package {
1097 found = true;
1098 let relation = relations.into_iter().next().unwrap();
1099
1100 let should_update = if let Some((vc, ver)) = relation.version() {
1102 match vc {
1103 VersionConstraint::GreaterThanEqual | VersionConstraint::GreaterThan => {
1104 &ver < minimum_version
1105 }
1106 _ => false,
1107 }
1108 } else {
1109 true
1110 };
1111
1112 if should_update {
1113 update_idx = Some(idx);
1114 }
1115 break;
1116 }
1117 }
1118
1119 if let Some(idx) = update_idx {
1121 let relation = Relation::new(
1122 package,
1123 Some((VersionConstraint::GreaterThanEqual, minimum_version.clone())),
1124 );
1125 let mut entry = self.get_entry(idx).unwrap();
1127 entry.replace(0, relation);
1128 self.replace(idx, entry);
1129 }
1130
1131 for idx in obsolete_indices.into_iter().rev() {
1133 self.remove_entry(idx);
1134 }
1135
1136 if !found {
1138 let relation = Relation::new(
1139 package,
1140 Some((VersionConstraint::GreaterThanEqual, minimum_version.clone())),
1141 );
1142 self.push(Entry::from(relation));
1143 }
1144 }
1145
1146 pub fn ensure_exact_version(&mut self, package: &str, version: &Version) {
1161 let mut found = false;
1162 let mut update_idx = None;
1163
1164 let entries: Vec<_> = self.entries().collect();
1165 for (idx, entry) in entries.iter().enumerate() {
1166 let relations: Vec<_> = entry.relations().collect();
1167 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1168
1169 if names.len() > 1 && names[0] == package {
1170 panic!("Complex rule for {}, aborting", package);
1171 }
1172
1173 if names.len() == 1 && names[0] == package {
1174 found = true;
1175 let relation = relations.into_iter().next().unwrap();
1176
1177 let should_update = if let Some((vc, ver)) = relation.version() {
1178 vc != VersionConstraint::Equal || &ver != version
1179 } else {
1180 true
1181 };
1182
1183 if should_update {
1184 update_idx = Some(idx);
1185 }
1186 break;
1187 }
1188 }
1189
1190 if let Some(idx) = update_idx {
1192 let relation =
1193 Relation::new(package, Some((VersionConstraint::Equal, version.clone())));
1194 let mut entry = self.get_entry(idx).unwrap();
1196 entry.replace(0, relation);
1197 self.replace(idx, entry);
1198 }
1199
1200 if !found {
1201 let relation =
1202 Relation::new(package, Some((VersionConstraint::Equal, version.clone())));
1203 self.push(Entry::from(relation));
1204 }
1205 }
1206
1207 pub fn ensure_some_version(&mut self, package: &str) {
1225 for entry in self.entries() {
1226 let relations: Vec<_> = entry.relations().collect();
1227 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1228
1229 if names.len() > 1 && names[0] == package {
1230 panic!("Complex rule for {}, aborting", package);
1231 }
1232
1233 if names.len() == 1 && names[0] == package {
1234 return;
1236 }
1237 }
1238
1239 let relation = Relation::simple(package);
1241 self.push(Entry::from(relation));
1242 }
1243
1244 pub fn ensure_relation(&mut self, new_entry: Entry) -> bool {
1269 let mut to_replace: Vec<usize> = Vec::new();
1270 let mut to_remove: Vec<usize> = Vec::new();
1271 let mut already_satisfied = false;
1272
1273 for (idx, existing_entry) in self.entries().enumerate() {
1275 if new_entry.is_implied_by(&existing_entry) {
1276 already_satisfied = true;
1278 break;
1279 }
1280 if existing_entry.is_implied_by(&new_entry) {
1281 if to_replace.is_empty() {
1284 to_replace.push(idx);
1285 } else {
1286 to_remove.push(idx);
1287 }
1288 }
1289 }
1290
1291 if already_satisfied {
1292 return false;
1293 }
1294
1295 for idx in to_remove.into_iter().rev() {
1297 self.remove_entry(idx);
1298 }
1299
1300 if let Some(&idx) = to_replace.first() {
1302 self.replace(idx, new_entry);
1303 } else {
1304 self.add_dependency(new_entry, None);
1305 }
1306
1307 true
1308 }
1309
1310 pub fn ensure_substvar(&mut self, substvar: &str) -> Result<(), String> {
1330 for existing in self.substvars() {
1332 if existing.trim() == substvar.trim() {
1333 return Ok(());
1334 }
1335 }
1336
1337 let (parsed, errors) = Relations::parse_relaxed(substvar, true);
1339 if !errors.is_empty() {
1340 return Err(errors.join("\n"));
1341 }
1342
1343 let whitespace = self.detect_whitespace_pattern(" ");
1345
1346 for substvar_node in parsed.0.children().filter(|n| n.kind() == SUBSTVAR) {
1348 let has_content = self.entries().next().is_some() || self.substvars().next().is_some();
1349
1350 let mut builder = GreenNodeBuilder::new();
1351 builder.start_node(ROOT.into());
1352
1353 for child in self.0.children_with_tokens() {
1355 match child {
1356 NodeOrToken::Node(n) => inject(&mut builder, n),
1357 NodeOrToken::Token(t) => builder.token(t.kind().into(), t.text()),
1358 }
1359 }
1360
1361 if has_content {
1363 builder.token(COMMA.into(), ",");
1364 builder.token(WHITESPACE.into(), whitespace.as_str());
1365 }
1366
1367 inject(&mut builder, substvar_node);
1369
1370 builder.finish_node();
1371 self.0 = SyntaxNode::new_root_mut(builder.finish());
1372 }
1373
1374 Ok(())
1375 }
1376
1377 pub fn drop_substvar(&mut self, substvar: &str) {
1394 let substvars_to_remove: Vec<_> = self
1396 .0
1397 .children()
1398 .filter_map(Substvar::cast)
1399 .filter(|s| s.to_string().trim() == substvar.trim())
1400 .collect();
1401
1402 for substvar_node in substvars_to_remove {
1403 let is_first = !substvar_node
1405 .0
1406 .siblings(Direction::Prev)
1407 .skip(1)
1408 .any(|n| n.kind() == ENTRY || n.kind() == SUBSTVAR);
1409
1410 let mut removed_comma = false;
1411
1412 while let Some(n) = substvar_node.0.next_sibling_or_token() {
1414 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1415 n.detach();
1416 } else if n.kind() == COMMA {
1417 n.detach();
1418 removed_comma = true;
1419 break;
1420 } else {
1421 break;
1422 }
1423 }
1424
1425 if !is_first {
1427 while let Some(n) = substvar_node.0.prev_sibling_or_token() {
1428 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1429 n.detach();
1430 } else if !removed_comma && n.kind() == COMMA {
1431 n.detach();
1432 break;
1433 } else {
1434 break;
1435 }
1436 }
1437 } else {
1438 while let Some(n) = substvar_node.0.next_sibling_or_token() {
1440 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1441 n.detach();
1442 } else {
1443 break;
1444 }
1445 }
1446 }
1447
1448 substvar_node.0.detach();
1450 }
1451 }
1452
1453 pub fn filter_entries<F>(&mut self, keep: F)
1469 where
1470 F: Fn(&Entry) -> bool,
1471 {
1472 let indices_to_remove: Vec<_> = self
1473 .entries()
1474 .enumerate()
1475 .filter_map(|(idx, entry)| if keep(&entry) { None } else { Some(idx) })
1476 .collect();
1477
1478 for idx in indices_to_remove.into_iter().rev() {
1480 self.remove_entry(idx);
1481 }
1482 }
1483
1484 pub fn is_sorted(&self, sorting_order: &impl SortingOrder) -> bool {
1500 let mut last_name: Option<String> = None;
1501 for entry in self.entries() {
1502 let mut relations = entry.relations();
1504 let Some(relation) = relations.next() else {
1505 continue;
1506 };
1507
1508 let name = relation.name();
1509
1510 if sorting_order.ignore(&name) {
1512 continue;
1513 }
1514
1515 if let Some(ref last) = last_name {
1517 if sorting_order.lt(&name, last) {
1518 return false;
1519 }
1520 }
1521
1522 last_name = Some(name);
1523 }
1524 true
1525 }
1526
1527 fn find_insert_position(&self, entry: &Entry) -> usize {
1539 let Some(relation) = entry.relations().next() else {
1541 return self.len();
1543 };
1544 let package_name = relation.name();
1545
1546 let count = self.entries().filter(|e| !e.is_empty()).count();
1548
1549 let sorting_order: Box<dyn SortingOrder> = if count < 2 {
1551 Box::new(WrapAndSortOrder)
1552 } else {
1553 if self.is_sorted(&WrapAndSortOrder) {
1556 Box::new(WrapAndSortOrder)
1557 } else if self.is_sorted(&DefaultSortingOrder) {
1558 Box::new(DefaultSortingOrder)
1559 } else {
1560 return self.len();
1562 }
1563 };
1564
1565 if sorting_order.ignore(&package_name) {
1567 return self.len();
1568 }
1569
1570 let mut position = 0;
1572 for (idx, existing_entry) in self.entries().enumerate() {
1573 let mut existing_relations = existing_entry.relations();
1574 let Some(existing_relation) = existing_relations.next() else {
1575 position += 1;
1577 continue;
1578 };
1579
1580 let existing_name = existing_relation.name();
1581
1582 if sorting_order.ignore(&existing_name) {
1584 position += 1;
1585 continue;
1586 }
1587
1588 if sorting_order.lt(&package_name, &existing_name) {
1590 return idx;
1591 }
1592 position += 1;
1593 }
1594
1595 position
1596 }
1597
1598 pub fn drop_dependency(&mut self, package: &str) -> bool {
1616 let indices_to_remove: Vec<_> = self
1617 .entries()
1618 .enumerate()
1619 .filter_map(|(idx, entry)| {
1620 let relations: Vec<_> = entry.relations().collect();
1621 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1622 if names == vec![package] {
1623 Some(idx)
1624 } else {
1625 None
1626 }
1627 })
1628 .collect();
1629
1630 let found = !indices_to_remove.is_empty();
1631
1632 for idx in indices_to_remove.into_iter().rev() {
1634 self.remove_entry(idx);
1635 }
1636
1637 found
1638 }
1639
1640 pub fn add_dependency(&mut self, entry: Entry, position: Option<usize>) {
1661 let pos = position.unwrap_or_else(|| self.find_insert_position(&entry));
1662 self.insert(pos, entry);
1663 }
1664
1665 pub fn get_relation(&self, package: &str) -> Result<(usize, Entry), String> {
1689 for (idx, entry) in self.entries().enumerate() {
1690 let relations: Vec<_> = entry.relations().collect();
1691 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1692
1693 if names.len() > 1 && names.contains(&package.to_string()) {
1694 return Err(format!("Complex rule for {}, aborting", package));
1695 }
1696
1697 if names.len() == 1 && names[0] == package {
1698 return Ok((idx, entry));
1699 }
1700 }
1701 Err(format!("Package {} not found", package))
1702 }
1703
1704 pub fn iter_relations_for(&self, package: &str) -> impl Iterator<Item = (usize, Entry)> + '_ {
1721 let package = package.to_string();
1722 self.entries().enumerate().filter(move |(_, entry)| {
1723 let names: Vec<_> = entry.relations().map(|r| r.name()).collect();
1724 names.contains(&package)
1725 })
1726 }
1727
1728 pub fn has_relation(&self, package: &str) -> bool {
1745 self.entries()
1746 .any(|entry| entry.relations().any(|r| r.name() == package))
1747 }
1748}
1749
1750impl From<Vec<Entry>> for Relations {
1751 fn from(entries: Vec<Entry>) -> Self {
1752 let mut builder = GreenNodeBuilder::new();
1753 builder.start_node(ROOT.into());
1754 for (i, entry) in entries.into_iter().enumerate() {
1755 if i > 0 {
1756 builder.token(COMMA.into(), ",");
1757 builder.token(WHITESPACE.into(), " ");
1758 }
1759 inject(&mut builder, entry.0);
1760 }
1761 builder.finish_node();
1762 Relations(SyntaxNode::new_root_mut(builder.finish()))
1763 }
1764}
1765
1766impl From<Entry> for Relations {
1767 fn from(entry: Entry) -> Self {
1768 Self::from(vec![entry])
1769 }
1770}
1771
1772impl Default for Entry {
1773 fn default() -> Self {
1774 Self::new()
1775 }
1776}
1777
1778impl PartialOrd for Entry {
1779 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1780 let mut rels_a = self.relations();
1781 let mut rels_b = other.relations();
1782 while let (Some(a), Some(b)) = (rels_a.next(), rels_b.next()) {
1783 match a.cmp(&b) {
1784 std::cmp::Ordering::Equal => continue,
1785 x => return Some(x),
1786 }
1787 }
1788
1789 if rels_a.next().is_some() {
1790 return Some(std::cmp::Ordering::Greater);
1791 }
1792
1793 if rels_b.next().is_some() {
1794 return Some(std::cmp::Ordering::Less);
1795 }
1796
1797 Some(std::cmp::Ordering::Equal)
1798 }
1799}
1800
1801impl Eq for Entry {}
1802
1803impl Ord for Entry {
1804 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1805 self.partial_cmp(other).unwrap()
1806 }
1807}
1808
1809impl Entry {
1810 pub fn new() -> Self {
1812 let mut builder = GreenNodeBuilder::new();
1813 builder.start_node(SyntaxKind::ENTRY.into());
1814 builder.finish_node();
1815 Entry(SyntaxNode::new_root_mut(builder.finish()))
1816 }
1817
1818 pub fn replace(&mut self, idx: usize, relation: Relation) {
1820 let current_relation = self.get_relation(idx).unwrap();
1821
1822 let old_root = current_relation.0;
1823 let new_root = relation.0;
1824 let mut prev = new_root.first_child_or_token();
1826 let mut new_head_len = 0;
1827 while let Some(p) = prev {
1829 if p.kind() == WHITESPACE || p.kind() == NEWLINE {
1830 new_head_len += 1;
1831 prev = p.next_sibling_or_token();
1832 } else {
1833 break;
1834 }
1835 }
1836 let mut new_tail_len = 0;
1837 let mut next = new_root.last_child_or_token();
1838 while let Some(n) = next {
1839 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1840 new_tail_len += 1;
1841 next = n.prev_sibling_or_token();
1842 } else {
1843 break;
1844 }
1845 }
1846 let mut prev = old_root.first_child_or_token();
1848 let mut old_head = vec![];
1849 while let Some(p) = prev {
1850 if p.kind() == WHITESPACE || p.kind() == NEWLINE {
1851 old_head.push(p.clone());
1852 prev = p.next_sibling_or_token();
1853 } else {
1854 break;
1855 }
1856 }
1857 let mut old_tail = vec![];
1858 let mut next = old_root.last_child_or_token();
1859 while let Some(n) = next {
1860 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1861 old_tail.push(n.clone());
1862 next = n.prev_sibling_or_token();
1863 } else {
1864 break;
1865 }
1866 }
1867 new_root.splice_children(0..new_head_len, old_head);
1868 let tail_pos = new_root.children_with_tokens().count() - new_tail_len;
1869 new_root.splice_children(
1870 tail_pos - new_tail_len..tail_pos,
1871 old_tail.into_iter().rev(),
1872 );
1873 let index = old_root.index();
1874 self.0
1875 .splice_children(index..index + 1, vec![new_root.into()]);
1876 }
1877
1878 #[must_use]
1880 pub fn wrap_and_sort(&self) -> Self {
1881 let mut relations = self
1882 .relations()
1883 .map(|r| r.wrap_and_sort())
1884 .collect::<Vec<_>>();
1885 relations.sort();
1887 Self::from(relations)
1888 }
1889
1890 pub fn relations(&self) -> impl Iterator<Item = Relation> + '_ {
1892 self.0.children().filter_map(Relation::cast)
1893 }
1894
1895 pub fn iter(&self) -> impl Iterator<Item = Relation> + '_ {
1897 self.relations()
1898 }
1899
1900 pub fn get_relation(&self, idx: usize) -> Option<Relation> {
1902 self.relations().nth(idx)
1903 }
1904
1905 pub fn remove_relation(&self, idx: usize) -> Relation {
1918 let mut relation = self.get_relation(idx).unwrap();
1919 relation.remove();
1920 relation
1921 }
1922
1923 pub fn satisfied_by(&self, package_version: impl crate::VersionLookup + Copy) -> bool {
1939 self.relations().any(|r| {
1940 let actual = package_version.lookup_version(r.name().as_str());
1941 if let Some((vc, version)) = r.version() {
1942 if let Some(actual) = actual {
1943 match vc {
1944 VersionConstraint::GreaterThanEqual => *actual >= version,
1945 VersionConstraint::LessThanEqual => *actual <= version,
1946 VersionConstraint::Equal => *actual == version,
1947 VersionConstraint::GreaterThan => *actual > version,
1948 VersionConstraint::LessThan => *actual < version,
1949 }
1950 } else {
1951 false
1952 }
1953 } else {
1954 actual.is_some()
1955 }
1956 })
1957 }
1958
1959 pub fn remove(&mut self) {
1970 let mut removed_comma = false;
1971 let is_first = !self
1972 .0
1973 .siblings(Direction::Prev)
1974 .skip(1)
1975 .any(|n| n.kind() == ENTRY);
1976 while let Some(n) = self.0.next_sibling_or_token() {
1977 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1978 n.detach();
1979 } else if n.kind() == COMMA {
1980 n.detach();
1981 removed_comma = true;
1982 break;
1983 } else {
1984 panic!("Unexpected node: {:?}", n);
1985 }
1986 }
1987 if !is_first {
1988 while let Some(n) = self.0.prev_sibling_or_token() {
1989 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1990 n.detach();
1991 } else if !removed_comma && n.kind() == COMMA {
1992 n.detach();
1993 break;
1994 } else {
1995 break;
1996 }
1997 }
1998 } else {
1999 while let Some(n) = self.0.next_sibling_or_token() {
2000 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2001 n.detach();
2002 } else {
2003 break;
2004 }
2005 }
2006 }
2007 self.0.detach();
2008 }
2009
2010 pub fn is_empty(&self) -> bool {
2012 self.relations().count() == 0
2013 }
2014
2015 pub fn len(&self) -> usize {
2017 self.relations().count()
2018 }
2019
2020 pub fn push(&mut self, relation: Relation) {
2033 let is_empty = !self
2034 .0
2035 .children_with_tokens()
2036 .any(|n| n.kind() == PIPE || n.kind() == RELATION);
2037
2038 let (position, new_children) = if let Some(current_relation) = self.relations().last() {
2039 let to_insert: Vec<NodeOrToken<GreenNode, GreenToken>> = if is_empty {
2040 vec![relation.0.green().into()]
2041 } else {
2042 vec![
2043 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
2044 NodeOrToken::Token(GreenToken::new(PIPE.into(), "|")),
2045 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
2046 relation.0.green().into(),
2047 ]
2048 };
2049
2050 (current_relation.0.index() + 1, to_insert)
2051 } else {
2052 let child_count = self.0.children_with_tokens().count();
2053 (
2054 child_count,
2055 if is_empty {
2056 vec![relation.0.green().into()]
2057 } else {
2058 vec![
2059 NodeOrToken::Token(GreenToken::new(PIPE.into(), "|")),
2060 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
2061 relation.0.green().into(),
2062 ]
2063 },
2064 )
2065 };
2066
2067 let new_root = SyntaxNode::new_root_mut(
2068 self.0.replace_with(
2069 self.0
2070 .green()
2071 .splice_children(position..position, new_children),
2072 ),
2073 );
2074
2075 if let Some(parent) = self.0.parent() {
2076 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2077 self.0 = parent
2078 .children_with_tokens()
2079 .nth(self.0.index())
2080 .unwrap()
2081 .clone()
2082 .into_node()
2083 .unwrap();
2084 } else {
2085 self.0 = new_root;
2086 }
2087 }
2088
2089 pub fn is_implied_by(&self, outer: &Entry) -> bool {
2114 if self == outer {
2116 return true;
2117 }
2118
2119 for inner_rel in self.relations() {
2121 for outer_rel in outer.relations() {
2122 if inner_rel.is_implied_by(&outer_rel) {
2123 return true;
2124 }
2125 }
2126 }
2127
2128 false
2129 }
2130}
2131
2132fn inject(builder: &mut GreenNodeBuilder, node: SyntaxNode) {
2133 builder.start_node(node.kind().into());
2134 for child in node.children_with_tokens() {
2135 match child {
2136 rowan::NodeOrToken::Node(child) => {
2137 inject(builder, child);
2138 }
2139 rowan::NodeOrToken::Token(token) => {
2140 builder.token(token.kind().into(), token.text());
2141 }
2142 }
2143 }
2144 builder.finish_node();
2145}
2146
2147impl From<Vec<Relation>> for Entry {
2148 fn from(relations: Vec<Relation>) -> Self {
2149 let mut builder = GreenNodeBuilder::new();
2150 builder.start_node(SyntaxKind::ENTRY.into());
2151 for (i, relation) in relations.into_iter().enumerate() {
2152 if i > 0 {
2153 builder.token(WHITESPACE.into(), " ");
2154 builder.token(COMMA.into(), "|");
2155 builder.token(WHITESPACE.into(), " ");
2156 }
2157 inject(&mut builder, relation.0);
2158 }
2159 builder.finish_node();
2160 Entry(SyntaxNode::new_root_mut(builder.finish()))
2161 }
2162}
2163
2164impl From<Relation> for Entry {
2165 fn from(relation: Relation) -> Self {
2166 Self::from(vec![relation])
2167 }
2168}
2169
2170fn tokenize_version(builder: &mut GreenNodeBuilder, version: &Version) {
2173 let version_str = version.to_string();
2174
2175 if let Some(colon_pos) = version_str.find(':') {
2177 builder.token(IDENT.into(), &version_str[..colon_pos]);
2179 builder.token(COLON.into(), ":");
2180 builder.token(IDENT.into(), &version_str[colon_pos + 1..]);
2182 } else {
2183 builder.token(IDENT.into(), version_str.as_str());
2185 }
2186}
2187
2188impl Relation {
2189 pub fn new(name: &str, version_constraint: Option<(VersionConstraint, Version)>) -> Self {
2203 let mut builder = GreenNodeBuilder::new();
2204 builder.start_node(SyntaxKind::RELATION.into());
2205 builder.token(IDENT.into(), name);
2206 if let Some((vc, version)) = version_constraint {
2207 builder.token(WHITESPACE.into(), " ");
2208 builder.start_node(SyntaxKind::VERSION.into());
2209 builder.token(L_PARENS.into(), "(");
2210 builder.start_node(SyntaxKind::CONSTRAINT.into());
2211 for c in vc.to_string().chars() {
2212 builder.token(
2213 match c {
2214 '>' => R_ANGLE.into(),
2215 '<' => L_ANGLE.into(),
2216 '=' => EQUAL.into(),
2217 _ => unreachable!(),
2218 },
2219 c.to_string().as_str(),
2220 );
2221 }
2222 builder.finish_node();
2223
2224 builder.token(WHITESPACE.into(), " ");
2225
2226 tokenize_version(&mut builder, &version);
2227
2228 builder.token(R_PARENS.into(), ")");
2229
2230 builder.finish_node();
2231 }
2232
2233 builder.finish_node();
2234 Relation(SyntaxNode::new_root_mut(builder.finish()))
2235 }
2236
2237 #[must_use]
2246 pub fn wrap_and_sort(&self) -> Self {
2247 let mut builder = GreenNodeBuilder::new();
2248 builder.start_node(SyntaxKind::RELATION.into());
2249 builder.token(IDENT.into(), self.name().as_str());
2250 if let Some(archqual) = self.archqual() {
2251 builder.token(COLON.into(), ":");
2252 builder.token(IDENT.into(), archqual.as_str());
2253 }
2254 if let Some((vc, version)) = self.version() {
2255 builder.token(WHITESPACE.into(), " ");
2256 builder.start_node(SyntaxKind::VERSION.into());
2257 builder.token(L_PARENS.into(), "(");
2258 builder.start_node(SyntaxKind::CONSTRAINT.into());
2259 builder.token(
2260 match vc {
2261 VersionConstraint::GreaterThanEqual => R_ANGLE.into(),
2262 VersionConstraint::LessThanEqual => L_ANGLE.into(),
2263 VersionConstraint::Equal => EQUAL.into(),
2264 VersionConstraint::GreaterThan => R_ANGLE.into(),
2265 VersionConstraint::LessThan => L_ANGLE.into(),
2266 },
2267 vc.to_string().as_str(),
2268 );
2269 builder.finish_node();
2270 builder.token(WHITESPACE.into(), " ");
2271 tokenize_version(&mut builder, &version);
2272 builder.token(R_PARENS.into(), ")");
2273 builder.finish_node();
2274 }
2275 if let Some(architectures) = self.architectures() {
2276 builder.token(WHITESPACE.into(), " ");
2277 builder.start_node(ARCHITECTURES.into());
2278 builder.token(L_BRACKET.into(), "[");
2279 for (i, arch) in architectures.enumerate() {
2280 if i > 0 {
2281 builder.token(WHITESPACE.into(), " ");
2282 }
2283 builder.token(IDENT.into(), arch.as_str());
2284 }
2285 builder.token(R_BRACKET.into(), "]");
2286 builder.finish_node();
2287 }
2288 for profiles in self.profiles() {
2289 builder.token(WHITESPACE.into(), " ");
2290 builder.start_node(PROFILES.into());
2291 builder.token(L_ANGLE.into(), "<");
2292 for (i, profile) in profiles.into_iter().enumerate() {
2293 if i > 0 {
2294 builder.token(WHITESPACE.into(), " ");
2295 }
2296 match profile {
2297 BuildProfile::Disabled(name) => {
2298 builder.token(NOT.into(), "!");
2299 builder.token(IDENT.into(), name.as_str());
2300 }
2301 BuildProfile::Enabled(name) => {
2302 builder.token(IDENT.into(), name.as_str());
2303 }
2304 }
2305 }
2306 builder.token(R_ANGLE.into(), ">");
2307 builder.finish_node();
2308 }
2309 builder.finish_node();
2310 Relation(SyntaxNode::new_root_mut(builder.finish()))
2311 }
2312
2313 pub fn simple(name: &str) -> Self {
2322 Self::new(name, None)
2323 }
2324
2325 pub fn drop_constraint(&mut self) -> bool {
2336 let version_token = self.0.children().find(|n| n.kind() == VERSION);
2337 if let Some(version_token) = version_token {
2338 while let Some(prev) = version_token.prev_sibling_or_token() {
2340 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
2341 prev.detach();
2342 } else {
2343 break;
2344 }
2345 }
2346 version_token.detach();
2347 return true;
2348 }
2349
2350 false
2351 }
2352
2353 pub fn name(&self) -> String {
2362 self.0
2363 .children_with_tokens()
2364 .find_map(|it| match it {
2365 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2366 _ => None,
2367 })
2368 .unwrap()
2369 .text()
2370 .to_string()
2371 }
2372
2373 pub fn archqual(&self) -> Option<String> {
2382 let archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
2383 let node = if let Some(archqual) = archqual {
2384 archqual.children_with_tokens().find_map(|it| match it {
2385 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2386 _ => None,
2387 })
2388 } else {
2389 None
2390 };
2391 node.map(|n| n.text().to_string())
2392 }
2393
2394 pub fn set_archqual(&mut self, archqual: &str) {
2404 let mut builder = GreenNodeBuilder::new();
2405 builder.start_node(ARCHQUAL.into());
2406 builder.token(COLON.into(), ":");
2407 builder.token(IDENT.into(), archqual);
2408 builder.finish_node();
2409
2410 let node_archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
2411 if let Some(node_archqual) = node_archqual {
2412 self.0.splice_children(
2413 node_archqual.index()..node_archqual.index() + 1,
2414 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2415 );
2416 } else {
2417 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
2418 let idx = if let Some(name_node) = name_node {
2419 name_node.index() + 1
2420 } else {
2421 0
2422 };
2423 self.0.splice_children(
2424 idx..idx,
2425 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2426 );
2427 }
2428 }
2429
2430 pub fn version(&self) -> Option<(VersionConstraint, Version)> {
2432 let vc = self.0.children().find(|n| n.kind() == VERSION);
2433 let vc = vc.as_ref()?;
2434 let constraint = vc.children().find(|n| n.kind() == CONSTRAINT);
2435
2436 let version_str: String = vc
2438 .children_with_tokens()
2439 .filter_map(|it| match it {
2440 SyntaxElement::Token(token) if token.kind() == IDENT || token.kind() == COLON => {
2441 Some(token.text().to_string())
2442 }
2443 _ => None,
2444 })
2445 .collect();
2446
2447 if let Some(constraint) = constraint {
2448 if !version_str.is_empty() {
2449 let vc: VersionConstraint = constraint.to_string().parse().unwrap();
2450 Some((vc, version_str.parse().unwrap()))
2451 } else {
2452 None
2453 }
2454 } else {
2455 None
2456 }
2457 }
2458
2459 pub fn set_version(&mut self, version_constraint: Option<(VersionConstraint, Version)>) {
2470 let current_version = self.0.children().find(|n| n.kind() == VERSION);
2471 if let Some((vc, version)) = version_constraint {
2472 let mut builder = GreenNodeBuilder::new();
2473 builder.start_node(VERSION.into());
2474 builder.token(L_PARENS.into(), "(");
2475 builder.start_node(CONSTRAINT.into());
2476 match vc {
2477 VersionConstraint::GreaterThanEqual => {
2478 builder.token(R_ANGLE.into(), ">");
2479 builder.token(EQUAL.into(), "=");
2480 }
2481 VersionConstraint::LessThanEqual => {
2482 builder.token(L_ANGLE.into(), "<");
2483 builder.token(EQUAL.into(), "=");
2484 }
2485 VersionConstraint::Equal => {
2486 builder.token(EQUAL.into(), "=");
2487 }
2488 VersionConstraint::GreaterThan => {
2489 builder.token(R_ANGLE.into(), ">");
2490 }
2491 VersionConstraint::LessThan => {
2492 builder.token(L_ANGLE.into(), "<");
2493 }
2494 }
2495 builder.finish_node(); builder.token(WHITESPACE.into(), " ");
2497 tokenize_version(&mut builder, &version);
2498 builder.token(R_PARENS.into(), ")");
2499 builder.finish_node(); if let Some(current_version) = current_version {
2502 self.0.splice_children(
2503 current_version.index()..current_version.index() + 1,
2504 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2505 );
2506 } else {
2507 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
2508 let idx = if let Some(name_node) = name_node {
2509 name_node.index() + 1
2510 } else {
2511 0
2512 };
2513 let new_children = vec![
2514 GreenToken::new(WHITESPACE.into(), " ").into(),
2515 builder.finish().into(),
2516 ];
2517 let new_root = SyntaxNode::new_root_mut(
2518 self.0.green().splice_children(idx..idx, new_children),
2519 );
2520 if let Some(parent) = self.0.parent() {
2521 parent
2522 .splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2523 self.0 = parent
2524 .children_with_tokens()
2525 .nth(self.0.index())
2526 .unwrap()
2527 .clone()
2528 .into_node()
2529 .unwrap();
2530 } else {
2531 self.0 = new_root;
2532 }
2533 }
2534 } else if let Some(current_version) = current_version {
2535 while let Some(prev) = current_version.prev_sibling_or_token() {
2537 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
2538 prev.detach();
2539 } else {
2540 break;
2541 }
2542 }
2543 current_version.detach();
2544 }
2545 }
2546
2547 pub fn architectures(&self) -> Option<impl Iterator<Item = String> + '_> {
2556 let architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES)?;
2557
2558 Some(architectures.children_with_tokens().filter_map(|node| {
2559 let token = node.as_token()?;
2560 if token.kind() == IDENT {
2561 Some(token.text().to_string())
2562 } else {
2563 None
2564 }
2565 }))
2566 }
2567
2568 pub fn profiles(&self) -> impl Iterator<Item = Vec<BuildProfile>> + '_ {
2578 let profiles = self.0.children().filter(|n| n.kind() == PROFILES);
2579
2580 profiles.map(|profile| {
2581 let mut ret = vec![];
2583 let mut current = vec![];
2584 for token in profile.children_with_tokens() {
2585 match token.kind() {
2586 WHITESPACE | NEWLINE => {
2587 if !current.is_empty() {
2588 ret.push(current.join("").parse::<BuildProfile>().unwrap());
2589 current = vec![];
2590 }
2591 }
2592 L_ANGLE | R_ANGLE => {}
2593 _ => {
2594 current.push(token.to_string());
2595 }
2596 }
2597 }
2598 if !current.is_empty() {
2599 ret.push(current.concat().parse().unwrap());
2600 }
2601 ret
2602 })
2603 }
2604
2605 pub fn remove(&mut self) {
2616 let is_first = !self
2617 .0
2618 .siblings(Direction::Prev)
2619 .skip(1)
2620 .any(|n| n.kind() == RELATION);
2621 if !is_first {
2622 while let Some(n) = self.0.prev_sibling_or_token() {
2625 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2626 n.detach();
2627 } else if n.kind() == PIPE {
2628 n.detach();
2629 break;
2630 } else {
2631 break;
2632 }
2633 }
2634 while let Some(n) = self.0.prev_sibling_or_token() {
2635 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2636 n.detach();
2637 } else {
2638 break;
2639 }
2640 }
2641 } else {
2642 while let Some(n) = self.0.next_sibling_or_token() {
2645 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2646 n.detach();
2647 } else if n.kind() == PIPE {
2648 n.detach();
2649 break;
2650 } else {
2651 panic!("Unexpected node: {:?}", n);
2652 }
2653 }
2654
2655 while let Some(n) = self.0.next_sibling_or_token() {
2656 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2657 n.detach();
2658 } else {
2659 break;
2660 }
2661 }
2662 }
2663 if let Some(mut parent) = self.0.parent().and_then(Entry::cast) {
2665 if parent.is_empty() {
2666 parent.remove();
2667 } else {
2668 self.0.detach();
2669 }
2670 } else {
2671 self.0.detach();
2672 }
2673 }
2674
2675 pub fn set_architectures<'a>(&mut self, architectures: impl Iterator<Item = &'a str>) {
2685 let mut builder = GreenNodeBuilder::new();
2686 builder.start_node(ARCHITECTURES.into());
2687 builder.token(L_BRACKET.into(), "[");
2688 for (i, arch) in architectures.enumerate() {
2689 if i > 0 {
2690 builder.token(WHITESPACE.into(), " ");
2691 }
2692 builder.token(IDENT.into(), arch);
2693 }
2694 builder.token(R_BRACKET.into(), "]");
2695 builder.finish_node();
2696
2697 let node_architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES);
2698 if let Some(node_architectures) = node_architectures {
2699 let new_root = SyntaxNode::new_root_mut(builder.finish());
2700 self.0.splice_children(
2701 node_architectures.index()..node_architectures.index() + 1,
2702 vec![new_root.into()],
2703 );
2704 } else {
2705 let profiles = self.0.children().find(|n| n.kind() == PROFILES);
2706 let idx = if let Some(profiles) = profiles {
2707 profiles.index()
2708 } else {
2709 self.0.children_with_tokens().count()
2710 };
2711 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
2712 idx..idx,
2713 vec![
2714 GreenToken::new(WHITESPACE.into(), " ").into(),
2715 builder.finish().into(),
2716 ],
2717 ));
2718 if let Some(parent) = self.0.parent() {
2719 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2720 self.0 = parent
2721 .children_with_tokens()
2722 .nth(self.0.index())
2723 .unwrap()
2724 .clone()
2725 .into_node()
2726 .unwrap();
2727 } else {
2728 self.0 = new_root;
2729 }
2730 }
2731 }
2732
2733 pub fn add_profile(&mut self, profile: &[BuildProfile]) {
2744 let mut builder = GreenNodeBuilder::new();
2745 builder.start_node(PROFILES.into());
2746 builder.token(L_ANGLE.into(), "<");
2747 for (i, profile) in profile.iter().enumerate() {
2748 if i > 0 {
2749 builder.token(WHITESPACE.into(), " ");
2750 }
2751 match profile {
2752 BuildProfile::Disabled(name) => {
2753 builder.token(NOT.into(), "!");
2754 builder.token(IDENT.into(), name.as_str());
2755 }
2756 BuildProfile::Enabled(name) => {
2757 builder.token(IDENT.into(), name.as_str());
2758 }
2759 }
2760 }
2761 builder.token(R_ANGLE.into(), ">");
2762 builder.finish_node();
2763
2764 let node_profiles = self.0.children().find(|n| n.kind() == PROFILES);
2765 if let Some(node_profiles) = node_profiles {
2766 let new_root = SyntaxNode::new_root_mut(builder.finish());
2767 self.0.splice_children(
2768 node_profiles.index()..node_profiles.index() + 1,
2769 vec![new_root.into()],
2770 );
2771 } else {
2772 let idx = self.0.children_with_tokens().count();
2773 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
2774 idx..idx,
2775 vec![
2776 GreenToken::new(WHITESPACE.into(), " ").into(),
2777 builder.finish().into(),
2778 ],
2779 ));
2780 if let Some(parent) = self.0.parent() {
2781 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2782 self.0 = parent
2783 .children_with_tokens()
2784 .nth(self.0.index())
2785 .unwrap()
2786 .clone()
2787 .into_node()
2788 .unwrap();
2789 } else {
2790 self.0 = new_root;
2791 }
2792 }
2793 }
2794
2795 pub fn build(name: &str) -> RelationBuilder {
2797 RelationBuilder::new(name)
2798 }
2799
2800 pub fn is_implied_by(&self, outer: &Relation) -> bool {
2827 if self.name() != outer.name() {
2828 return false;
2829 }
2830
2831 let inner_version = self.version();
2832 let outer_version = outer.version();
2833
2834 if inner_version.is_none() {
2836 return true;
2837 }
2838
2839 if inner_version == outer_version {
2841 return true;
2842 }
2843
2844 if outer_version.is_none() {
2846 return false;
2847 }
2848
2849 let (inner_constraint, inner_ver) = inner_version.unwrap();
2850 let (outer_constraint, outer_ver) = outer_version.unwrap();
2851
2852 use VersionConstraint::*;
2853 match inner_constraint {
2854 GreaterThanEqual => match outer_constraint {
2855 GreaterThan => outer_ver > inner_ver,
2856 GreaterThanEqual | Equal => outer_ver >= inner_ver,
2857 LessThan | LessThanEqual => false,
2858 },
2859 Equal => match outer_constraint {
2860 Equal => outer_ver == inner_ver,
2861 _ => false,
2862 },
2863 LessThan => match outer_constraint {
2864 LessThan => outer_ver <= inner_ver,
2865 LessThanEqual | Equal => outer_ver < inner_ver,
2866 GreaterThan | GreaterThanEqual => false,
2867 },
2868 LessThanEqual => match outer_constraint {
2869 LessThanEqual | Equal | LessThan => outer_ver <= inner_ver,
2870 GreaterThan | GreaterThanEqual => false,
2871 },
2872 GreaterThan => match outer_constraint {
2873 GreaterThan => outer_ver >= inner_ver,
2874 Equal | GreaterThanEqual => outer_ver > inner_ver,
2875 LessThan | LessThanEqual => false,
2876 },
2877 }
2878 }
2879}
2880
2881pub struct RelationBuilder {
2895 name: String,
2896 version_constraint: Option<(VersionConstraint, Version)>,
2897 archqual: Option<String>,
2898 architectures: Option<Vec<String>>,
2899 profiles: Vec<Vec<BuildProfile>>,
2900}
2901
2902impl RelationBuilder {
2903 fn new(name: &str) -> Self {
2905 Self {
2906 name: name.to_string(),
2907 version_constraint: None,
2908 archqual: None,
2909 architectures: None,
2910 profiles: vec![],
2911 }
2912 }
2913
2914 pub fn version_constraint(mut self, vc: VersionConstraint, version: Version) -> Self {
2916 self.version_constraint = Some((vc, version));
2917 self
2918 }
2919
2920 pub fn archqual(mut self, archqual: &str) -> Self {
2922 self.archqual = Some(archqual.to_string());
2923 self
2924 }
2925
2926 pub fn architectures(mut self, architectures: Vec<String>) -> Self {
2928 self.architectures = Some(architectures);
2929 self
2930 }
2931
2932 pub fn profiles(mut self, profiles: Vec<Vec<BuildProfile>>) -> Self {
2934 self.profiles = profiles;
2935 self
2936 }
2937
2938 pub fn add_profile(mut self, profile: Vec<BuildProfile>) -> Self {
2940 self.profiles.push(profile);
2941 self
2942 }
2943
2944 pub fn build(self) -> Relation {
2946 let mut relation = Relation::new(&self.name, self.version_constraint);
2947 if let Some(archqual) = &self.archqual {
2948 relation.set_archqual(archqual);
2949 }
2950 if let Some(architectures) = &self.architectures {
2951 relation.set_architectures(architectures.iter().map(|s| s.as_str()));
2952 }
2953 for profile in &self.profiles {
2954 relation.add_profile(profile);
2955 }
2956 relation
2957 }
2958}
2959
2960impl PartialOrd for Relation {
2961 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
2962 let name_cmp = self.name().cmp(&other.name());
2964 if name_cmp != std::cmp::Ordering::Equal {
2965 return Some(name_cmp);
2966 }
2967
2968 let self_version = self.version();
2969 let other_version = other.version();
2970
2971 match (self_version, other_version) {
2972 (Some((self_vc, self_version)), Some((other_vc, other_version))) => {
2973 let vc_cmp = self_vc.cmp(&other_vc);
2974 if vc_cmp != std::cmp::Ordering::Equal {
2975 return Some(vc_cmp);
2976 }
2977
2978 Some(self_version.cmp(&other_version))
2979 }
2980 (Some(_), None) => Some(std::cmp::Ordering::Greater),
2981 (None, Some(_)) => Some(std::cmp::Ordering::Less),
2982 (None, None) => Some(std::cmp::Ordering::Equal),
2983 }
2984 }
2985}
2986
2987impl Eq for Relation {}
2988
2989impl Ord for Relation {
2990 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
2991 self.partial_cmp(other).unwrap()
2992 }
2993}
2994
2995impl std::str::FromStr for Relations {
2996 type Err = String;
2997
2998 fn from_str(s: &str) -> Result<Self, Self::Err> {
2999 let parse = parse(s, false);
3000 if parse.errors.is_empty() {
3001 Ok(parse.root_mut())
3002 } else {
3003 Err(parse.errors.join("\n"))
3004 }
3005 }
3006}
3007
3008impl std::str::FromStr for Entry {
3009 type Err = String;
3010
3011 fn from_str(s: &str) -> Result<Self, Self::Err> {
3012 let root: Relations = s.parse()?;
3013
3014 let mut entries = root.entries();
3015 let entry = if let Some(entry) = entries.next() {
3016 entry
3017 } else {
3018 return Err("No entry found".to_string());
3019 };
3020
3021 if entries.next().is_some() {
3022 return Err("Multiple entries found".to_string());
3023 }
3024
3025 Ok(entry)
3026 }
3027}
3028
3029impl std::str::FromStr for Relation {
3030 type Err = String;
3031
3032 fn from_str(s: &str) -> Result<Self, Self::Err> {
3033 let entry: Entry = s.parse()?;
3034
3035 let mut relations = entry.relations();
3036 let relation = if let Some(relation) = relations.next() {
3037 relation
3038 } else {
3039 return Err("No relation found".to_string());
3040 };
3041
3042 if relations.next().is_some() {
3043 return Err("Multiple relations found".to_string());
3044 }
3045
3046 Ok(relation)
3047 }
3048}
3049
3050impl From<crate::lossy::Relation> for Relation {
3051 fn from(relation: crate::lossy::Relation) -> Self {
3052 let mut builder = Relation::build(&relation.name);
3053
3054 if let Some((vc, version)) = relation.version {
3055 builder = builder.version_constraint(vc, version);
3056 }
3057
3058 if let Some(archqual) = relation.archqual {
3059 builder = builder.archqual(&archqual);
3060 }
3061
3062 if let Some(architectures) = relation.architectures {
3063 builder = builder.architectures(architectures);
3064 }
3065
3066 builder = builder.profiles(relation.profiles);
3067
3068 builder.build()
3069 }
3070}
3071
3072impl From<Relation> for crate::lossy::Relation {
3073 fn from(relation: Relation) -> Self {
3074 crate::lossy::Relation {
3075 name: relation.name(),
3076 version: relation.version(),
3077 archqual: relation.archqual(),
3078 architectures: relation.architectures().map(|a| a.collect()),
3079 profiles: relation.profiles().collect(),
3080 }
3081 }
3082}
3083
3084impl From<Entry> for Vec<crate::lossy::Relation> {
3085 fn from(entry: Entry) -> Self {
3086 entry.relations().map(|r| r.into()).collect()
3087 }
3088}
3089
3090impl From<Vec<crate::lossy::Relation>> for Entry {
3091 fn from(relations: Vec<crate::lossy::Relation>) -> Self {
3092 let relations: Vec<Relation> = relations.into_iter().map(|r| r.into()).collect();
3093 Entry::from(relations)
3094 }
3095}
3096
3097#[cfg(test)]
3098mod tests {
3099 use super::*;
3100
3101 #[test]
3102 fn test_parse() {
3103 let input = "python3-dulwich";
3104 let parsed: Relations = input.parse().unwrap();
3105 assert_eq!(parsed.to_string(), input);
3106 assert_eq!(parsed.entries().count(), 1);
3107 let entry = parsed.entries().next().unwrap();
3108 assert_eq!(entry.to_string(), "python3-dulwich");
3109 assert_eq!(entry.relations().count(), 1);
3110 let relation = entry.relations().next().unwrap();
3111 assert_eq!(relation.to_string(), "python3-dulwich");
3112 assert_eq!(relation.version(), None);
3113
3114 let input = "python3-dulwich (>= 0.20.21)";
3115 let parsed: Relations = input.parse().unwrap();
3116 assert_eq!(parsed.to_string(), input);
3117 assert_eq!(parsed.entries().count(), 1);
3118 let entry = parsed.entries().next().unwrap();
3119 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3120 assert_eq!(entry.relations().count(), 1);
3121 let relation = entry.relations().next().unwrap();
3122 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
3123 assert_eq!(
3124 relation.version(),
3125 Some((
3126 VersionConstraint::GreaterThanEqual,
3127 "0.20.21".parse().unwrap()
3128 ))
3129 );
3130 }
3131
3132 #[test]
3133 fn test_multiple() {
3134 let input = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)";
3135 let parsed: Relations = input.parse().unwrap();
3136 assert_eq!(parsed.to_string(), input);
3137 assert_eq!(parsed.entries().count(), 2);
3138 let entry = parsed.entries().next().unwrap();
3139 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3140 assert_eq!(entry.relations().count(), 1);
3141 let relation = entry.relations().next().unwrap();
3142 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
3143 assert_eq!(
3144 relation.version(),
3145 Some((
3146 VersionConstraint::GreaterThanEqual,
3147 "0.20.21".parse().unwrap()
3148 ))
3149 );
3150 let entry = parsed.entries().nth(1).unwrap();
3151 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.21)");
3152 assert_eq!(entry.relations().count(), 1);
3153 let relation = entry.relations().next().unwrap();
3154 assert_eq!(relation.to_string(), "python3-dulwich (<< 0.21)");
3155 assert_eq!(
3156 relation.version(),
3157 Some((VersionConstraint::LessThan, "0.21".parse().unwrap()))
3158 );
3159 }
3160
3161 #[test]
3162 fn test_architectures() {
3163 let input = "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]";
3164 let parsed: Relations = input.parse().unwrap();
3165 assert_eq!(parsed.to_string(), input);
3166 assert_eq!(parsed.entries().count(), 1);
3167 let entry = parsed.entries().next().unwrap();
3168 assert_eq!(
3169 entry.to_string(),
3170 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
3171 );
3172 assert_eq!(entry.relations().count(), 1);
3173 let relation = entry.relations().next().unwrap();
3174 assert_eq!(
3175 relation.to_string(),
3176 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
3177 );
3178 assert_eq!(relation.version(), None);
3179 assert_eq!(
3180 relation.architectures().unwrap().collect::<Vec<_>>(),
3181 vec![
3182 "amd64", "arm64", "armhf", "i386", "mips", "mips64el", "mipsel", "ppc64el", "s390x"
3183 ]
3184 .into_iter()
3185 .map(|s| s.to_string())
3186 .collect::<Vec<_>>()
3187 );
3188 }
3189
3190 #[test]
3191 fn test_profiles() {
3192 let input = "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>, bar";
3193 let parsed: Relations = input.parse().unwrap();
3194 assert_eq!(parsed.to_string(), input);
3195 assert_eq!(parsed.entries().count(), 2);
3196 let entry = parsed.entries().next().unwrap();
3197 assert_eq!(
3198 entry.to_string(),
3199 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
3200 );
3201 assert_eq!(entry.relations().count(), 1);
3202 let relation = entry.relations().next().unwrap();
3203 assert_eq!(
3204 relation.to_string(),
3205 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
3206 );
3207 assert_eq!(
3208 relation.version(),
3209 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap()))
3210 );
3211 assert_eq!(
3212 relation.architectures().unwrap().collect::<Vec<_>>(),
3213 vec!["i386", "arm"]
3214 .into_iter()
3215 .map(|s| s.to_string())
3216 .collect::<Vec<_>>()
3217 );
3218 assert_eq!(
3219 relation.profiles().collect::<Vec<_>>(),
3220 vec![
3221 vec![BuildProfile::Disabled("nocheck".to_string())],
3222 vec![BuildProfile::Disabled("cross".to_string())]
3223 ]
3224 );
3225 }
3226
3227 #[test]
3228 fn test_substvar() {
3229 let input = "${shlibs:Depends}";
3230
3231 let (parsed, errors) = Relations::parse_relaxed(input, true);
3232 assert_eq!(errors, Vec::<String>::new());
3233 assert_eq!(parsed.to_string(), input);
3234 assert_eq!(parsed.entries().count(), 0);
3235
3236 assert_eq!(
3237 parsed.substvars().collect::<Vec<_>>(),
3238 vec!["${shlibs:Depends}"]
3239 );
3240 }
3241
3242 #[test]
3243 fn test_new() {
3244 let r = Relation::new(
3245 "samba",
3246 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
3247 );
3248
3249 assert_eq!(r.to_string(), "samba (>= 2.0)");
3250 }
3251
3252 #[test]
3253 fn test_drop_constraint() {
3254 let mut r = Relation::new(
3255 "samba",
3256 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
3257 );
3258
3259 r.drop_constraint();
3260
3261 assert_eq!(r.to_string(), "samba");
3262 }
3263
3264 #[test]
3265 fn test_simple() {
3266 let r = Relation::simple("samba");
3267
3268 assert_eq!(r.to_string(), "samba");
3269 }
3270
3271 #[test]
3272 fn test_remove_first_entry() {
3273 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3274 .parse()
3275 .unwrap();
3276 let removed = rels.remove_entry(0);
3277 assert_eq!(removed.to_string(), "python3-dulwich (>= 0.20.21)");
3278 assert_eq!(rels.to_string(), "python3-dulwich (<< 0.21)");
3279 }
3280
3281 #[test]
3282 fn test_remove_last_entry() {
3283 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3284 .parse()
3285 .unwrap();
3286 rels.remove_entry(1);
3287 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
3288 }
3289
3290 #[test]
3291 fn test_remove_middle() {
3292 let mut rels: Relations =
3293 r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21), python3-dulwich (<< 0.22)"#
3294 .parse()
3295 .unwrap();
3296 rels.remove_entry(1);
3297 assert_eq!(
3298 rels.to_string(),
3299 "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.22)"
3300 );
3301 }
3302
3303 #[test]
3304 fn test_remove_added() {
3305 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
3306 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3307 rels.push(entry);
3308 rels.remove_entry(1);
3309 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
3310 }
3311
3312 #[test]
3313 fn test_push() {
3314 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
3315 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3316 rels.push(entry);
3317 assert_eq!(
3318 rels.to_string(),
3319 "python3-dulwich (>= 0.20.21), python3-dulwich"
3320 );
3321 }
3322
3323 #[test]
3324 fn test_insert_with_custom_separator() {
3325 let mut rels: Relations = "python3".parse().unwrap();
3326 let entry = Entry::from(vec![Relation::simple("debhelper")]);
3327 rels.insert_with_separator(1, entry, Some("\n "));
3328 assert_eq!(rels.to_string(), "python3,\n debhelper");
3329 }
3330
3331 #[test]
3332 fn test_insert_with_wrap_and_sort_separator() {
3333 let mut rels: Relations = "python3".parse().unwrap();
3334 let entry = Entry::from(vec![Relation::simple("rustc")]);
3335 rels.insert_with_separator(1, entry, Some("\n "));
3337 assert_eq!(rels.to_string(), "python3,\n rustc");
3338 }
3339
3340 #[test]
3341 fn test_push_from_empty() {
3342 let mut rels: Relations = "".parse().unwrap();
3343 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3344 rels.push(entry);
3345 assert_eq!(rels.to_string(), "python3-dulwich");
3346 }
3347
3348 #[test]
3349 fn test_insert() {
3350 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3351 .parse()
3352 .unwrap();
3353 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3354 rels.insert(1, entry);
3355 assert_eq!(
3356 rels.to_string(),
3357 "python3-dulwich (>= 0.20.21), python3-dulwich, python3-dulwich (<< 0.21)"
3358 );
3359 }
3360
3361 #[test]
3362 fn test_insert_at_start() {
3363 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3364 .parse()
3365 .unwrap();
3366 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3367 rels.insert(0, entry);
3368 assert_eq!(
3369 rels.to_string(),
3370 "python3-dulwich, python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3371 );
3372 }
3373
3374 #[test]
3375 fn test_insert_after_error() {
3376 let (mut rels, errors) = Relations::parse_relaxed("@foo@, debhelper (>= 1.0)", false);
3377 assert_eq!(
3378 errors,
3379 vec![
3380 "expected $ or identifier but got ERROR",
3381 "expected comma or end of file but got Some(IDENT)",
3382 "expected $ or identifier but got ERROR"
3383 ]
3384 );
3385 let entry = Entry::from(vec![Relation::simple("bar")]);
3386 rels.push(entry);
3387 assert_eq!(rels.to_string(), "@foo@, debhelper (>= 1.0), bar");
3388 }
3389
3390 #[test]
3391 fn test_insert_before_error() {
3392 let (mut rels, errors) = Relations::parse_relaxed("debhelper (>= 1.0), @foo@, bla", false);
3393 assert_eq!(
3394 errors,
3395 vec![
3396 "expected $ or identifier but got ERROR",
3397 "expected comma or end of file but got Some(IDENT)",
3398 "expected $ or identifier but got ERROR"
3399 ]
3400 );
3401 let entry = Entry::from(vec![Relation::simple("bar")]);
3402 rels.insert(0, entry);
3403 assert_eq!(rels.to_string(), "bar, debhelper (>= 1.0), @foo@, bla");
3404 }
3405
3406 #[test]
3407 fn test_replace() {
3408 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3409 .parse()
3410 .unwrap();
3411 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3412 rels.replace(1, entry);
3413 assert_eq!(
3414 rels.to_string(),
3415 "python3-dulwich (>= 0.20.21), python3-dulwich"
3416 );
3417 }
3418
3419 #[test]
3420 fn test_relation_from_entries() {
3421 let entries = vec![
3422 Entry::from(vec![Relation::simple("python3-dulwich")]),
3423 Entry::from(vec![Relation::simple("python3-breezy")]),
3424 ];
3425 let rels: Relations = entries.into();
3426 assert_eq!(rels.entries().count(), 2);
3427 assert_eq!(rels.to_string(), "python3-dulwich, python3-breezy");
3428 }
3429
3430 #[test]
3431 fn test_entry_from_relations() {
3432 let relations = vec![
3433 Relation::simple("python3-dulwich"),
3434 Relation::simple("python3-breezy"),
3435 ];
3436 let entry: Entry = relations.into();
3437 assert_eq!(entry.relations().count(), 2);
3438 assert_eq!(entry.to_string(), "python3-dulwich | python3-breezy");
3439 }
3440
3441 #[test]
3442 fn test_parse_entry() {
3443 let parsed: Entry = "python3-dulwich (>= 0.20.21) | bar".parse().unwrap();
3444 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21) | bar");
3445 assert_eq!(parsed.relations().count(), 2);
3446
3447 assert_eq!(
3448 "foo, bar".parse::<Entry>().unwrap_err(),
3449 "Multiple entries found"
3450 );
3451 assert_eq!("".parse::<Entry>().unwrap_err(), "No entry found");
3452 }
3453
3454 #[test]
3455 fn test_parse_relation() {
3456 let parsed: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3457 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21)");
3458 assert_eq!(
3459 parsed.version(),
3460 Some((
3461 VersionConstraint::GreaterThanEqual,
3462 "0.20.21".parse().unwrap()
3463 ))
3464 );
3465 assert_eq!(
3466 "foo | bar".parse::<Relation>().unwrap_err(),
3467 "Multiple relations found"
3468 );
3469 assert_eq!("".parse::<Relation>().unwrap_err(), "No entry found");
3470 }
3471
3472 #[test]
3473 fn test_special() {
3474 let parsed: Relation = "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
3475 .parse()
3476 .unwrap();
3477 assert_eq!(
3478 parsed.to_string(),
3479 "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
3480 );
3481 assert_eq!(
3482 parsed.version(),
3483 Some((
3484 VersionConstraint::GreaterThanEqual,
3485 "0.1.138-~~".parse().unwrap()
3486 ))
3487 );
3488 assert_eq!(parsed.archqual(), Some("amd64".to_string()));
3489 assert_eq!(parsed.name(), "librust-breezyshim+dirty-tracker-dev");
3490 }
3491
3492 #[test]
3493 fn test_relations_satisfied_by() {
3494 let rels: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3495 .parse()
3496 .unwrap();
3497 let satisfied = |name: &str| -> Option<debversion::Version> {
3498 match name {
3499 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
3500 _ => None,
3501 }
3502 };
3503 assert!(rels.satisfied_by(satisfied));
3504
3505 let satisfied = |name: &str| match name {
3506 "python3-dulwich" => Some("0.21".parse().unwrap()),
3507 _ => None,
3508 };
3509 assert!(!rels.satisfied_by(satisfied));
3510
3511 let satisfied = |name: &str| match name {
3512 "python3-dulwich" => Some("0.20.20".parse().unwrap()),
3513 _ => None,
3514 };
3515 assert!(!rels.satisfied_by(satisfied));
3516 }
3517
3518 #[test]
3519 fn test_entry_satisfied_by() {
3520 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3521 .parse()
3522 .unwrap();
3523 let satisfied = |name: &str| -> Option<debversion::Version> {
3524 match name {
3525 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
3526 _ => None,
3527 }
3528 };
3529 assert!(entry.satisfied_by(satisfied));
3530 let satisfied = |name: &str| -> Option<debversion::Version> {
3531 match name {
3532 "python3-dulwich" => Some("0.18".parse().unwrap()),
3533 _ => None,
3534 }
3535 };
3536 assert!(!entry.satisfied_by(satisfied));
3537 }
3538
3539 #[test]
3540 fn test_wrap_and_sort_relation() {
3541 let relation: Relation = " python3-dulwich (>= 11) [ amd64 ] < lala>"
3542 .parse()
3543 .unwrap();
3544
3545 let wrapped = relation.wrap_and_sort();
3546
3547 assert_eq!(
3548 wrapped.to_string(),
3549 "python3-dulwich (>= 11) [amd64] <lala>"
3550 );
3551 }
3552
3553 #[test]
3554 fn test_wrap_and_sort_relations() {
3555 let entry: Relations =
3556 "python3-dulwich (>= 0.20.21) | bar, \n\n\n\npython3-dulwich (<< 0.21)"
3557 .parse()
3558 .unwrap();
3559
3560 let wrapped = entry.wrap_and_sort();
3561
3562 assert_eq!(
3563 wrapped.to_string(),
3564 "bar | python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3565 );
3566 }
3567
3568 #[cfg(feature = "serde")]
3569 #[test]
3570 fn test_serialize_relations() {
3571 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3572 .parse()
3573 .unwrap();
3574 let serialized = serde_json::to_string(&relations).unwrap();
3575 assert_eq!(
3576 serialized,
3577 r#""python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)""#
3578 );
3579 }
3580
3581 #[cfg(feature = "serde")]
3582 #[test]
3583 fn test_deserialize_relations() {
3584 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3585 .parse()
3586 .unwrap();
3587 let serialized = serde_json::to_string(&relations).unwrap();
3588 let deserialized: Relations = serde_json::from_str(&serialized).unwrap();
3589 assert_eq!(deserialized.to_string(), relations.to_string());
3590 }
3591
3592 #[cfg(feature = "serde")]
3593 #[test]
3594 fn test_serialize_relation() {
3595 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3596 let serialized = serde_json::to_string(&relation).unwrap();
3597 assert_eq!(serialized, r#""python3-dulwich (>= 0.20.21)""#);
3598 }
3599
3600 #[cfg(feature = "serde")]
3601 #[test]
3602 fn test_deserialize_relation() {
3603 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3604 let serialized = serde_json::to_string(&relation).unwrap();
3605 let deserialized: Relation = serde_json::from_str(&serialized).unwrap();
3606 assert_eq!(deserialized.to_string(), relation.to_string());
3607 }
3608
3609 #[cfg(feature = "serde")]
3610 #[test]
3611 fn test_serialize_entry() {
3612 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3613 .parse()
3614 .unwrap();
3615 let serialized = serde_json::to_string(&entry).unwrap();
3616 assert_eq!(
3617 serialized,
3618 r#""python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)""#
3619 );
3620 }
3621
3622 #[cfg(feature = "serde")]
3623 #[test]
3624 fn test_deserialize_entry() {
3625 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3626 .parse()
3627 .unwrap();
3628 let serialized = serde_json::to_string(&entry).unwrap();
3629 let deserialized: Entry = serde_json::from_str(&serialized).unwrap();
3630 assert_eq!(deserialized.to_string(), entry.to_string());
3631 }
3632
3633 #[test]
3634 fn test_remove_first_relation() {
3635 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3636 .parse()
3637 .unwrap();
3638 let mut rel = entry.relations().next().unwrap();
3639 rel.remove();
3640 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.18)");
3641 }
3642
3643 #[test]
3644 fn test_remove_last_relation() {
3645 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3646 .parse()
3647 .unwrap();
3648 let mut rel = entry.relations().nth(1).unwrap();
3649 rel.remove();
3650 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3651 }
3652
3653 #[test]
3654 fn test_remove_only_relation() {
3655 let entry: Entry = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3656 let mut rel = entry.relations().next().unwrap();
3657 rel.remove();
3658 assert_eq!(entry.to_string(), "");
3659 }
3660
3661 #[test]
3662 fn test_relations_is_empty() {
3663 let entry: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3664 assert!(!entry.is_empty());
3665 assert_eq!(1, entry.len());
3666 let mut rel = entry.entries().next().unwrap();
3667 rel.remove();
3668 assert!(entry.is_empty());
3669 assert_eq!(0, entry.len());
3670 }
3671
3672 #[test]
3673 fn test_entry_is_empty() {
3674 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3675 .parse()
3676 .unwrap();
3677 assert!(!entry.is_empty());
3678 assert_eq!(2, entry.len());
3679 let mut rel = entry.relations().next().unwrap();
3680 rel.remove();
3681 assert!(!entry.is_empty());
3682 assert_eq!(1, entry.len());
3683 let mut rel = entry.relations().next().unwrap();
3684 rel.remove();
3685 assert!(entry.is_empty());
3686 assert_eq!(0, entry.len());
3687 }
3688
3689 #[test]
3690 fn test_relation_set_version() {
3691 let mut rel: Relation = "samba".parse().unwrap();
3692 rel.set_version(None);
3693 assert_eq!("samba", rel.to_string());
3694 rel.set_version(Some((
3695 VersionConstraint::GreaterThanEqual,
3696 "2.0".parse().unwrap(),
3697 )));
3698 assert_eq!("samba (>= 2.0)", rel.to_string());
3699 rel.set_version(None);
3700 assert_eq!("samba", rel.to_string());
3701 rel.set_version(Some((
3702 VersionConstraint::GreaterThanEqual,
3703 "2.0".parse().unwrap(),
3704 )));
3705 rel.set_version(Some((
3706 VersionConstraint::GreaterThanEqual,
3707 "1.1".parse().unwrap(),
3708 )));
3709 assert_eq!("samba (>= 1.1)", rel.to_string());
3710 }
3711
3712 #[test]
3713 fn test_replace_relation() {
3714 let mut entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3715 .parse()
3716 .unwrap();
3717 let new_rel = Relation::simple("python3-breezy");
3718 entry.replace(0, new_rel);
3719 assert_eq!(
3720 entry.to_string(),
3721 "python3-breezy | python3-dulwich (<< 0.18)"
3722 );
3723 }
3724
3725 #[test]
3726 fn test_entry_push_relation() {
3727 let relations: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3728 let new_rel = Relation::simple("python3-breezy");
3729 let mut entry = relations.entries().next().unwrap();
3730 entry.push(new_rel);
3731 assert_eq!(
3732 entry.to_string(),
3733 "python3-dulwich (>= 0.20.21) | python3-breezy"
3734 );
3735 assert_eq!(
3736 relations.to_string(),
3737 "python3-dulwich (>= 0.20.21) | python3-breezy"
3738 );
3739 }
3740
3741 #[test]
3742 fn test_relations_remove_empty_entry() {
3743 let (mut relations, errors) = Relations::parse_relaxed("foo, , bar, ", false);
3744 assert_eq!(errors, Vec::<String>::new());
3745 assert_eq!(relations.to_string(), "foo, , bar, ");
3746 assert_eq!(relations.len(), 2);
3747 assert_eq!(
3748 relations.entries().next().unwrap().to_string(),
3749 "foo".to_string()
3750 );
3751 assert_eq!(
3752 relations.entries().nth(1).unwrap().to_string(),
3753 "bar".to_string()
3754 );
3755 relations.remove_entry(1);
3756 assert_eq!(relations.to_string(), "foo, , ");
3757 }
3758
3759 #[test]
3760 fn test_entry_remove_relation() {
3761 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
3762 let removed = entry.remove_relation(0);
3763 assert_eq!(removed.to_string(), "python3-dulwich");
3764 assert_eq!(entry.to_string(), "samba");
3765 }
3766
3767 #[test]
3768 fn test_wrap_and_sort_removes_empty_entries() {
3769 let relations: Relations = "foo, , bar, ".parse().unwrap();
3770 let wrapped = relations.wrap_and_sort();
3771 assert_eq!(wrapped.to_string(), "bar, foo");
3772 }
3773
3774 #[test]
3775 fn test_set_archqual() {
3776 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
3777 let mut rel = entry.relations().next().unwrap();
3778 rel.set_archqual("amd64");
3779 assert_eq!(rel.to_string(), "python3-dulwich:amd64");
3780 assert_eq!(rel.archqual(), Some("amd64".to_string()));
3781 assert_eq!(entry.to_string(), "python3-dulwich:amd64 | samba");
3782 rel.set_archqual("i386");
3783 assert_eq!(rel.to_string(), "python3-dulwich:i386");
3784 assert_eq!(rel.archqual(), Some("i386".to_string()));
3785 assert_eq!(entry.to_string(), "python3-dulwich:i386 | samba");
3786 }
3787
3788 #[test]
3789 fn test_set_architectures() {
3790 let mut relation = Relation::simple("samba");
3791 relation.set_architectures(vec!["amd64", "i386"].into_iter());
3792 assert_eq!(relation.to_string(), "samba [amd64 i386]");
3793 }
3794
3795 #[test]
3796 fn test_relation_builder_no_architectures() {
3797 let relation = Relation::build("debhelper").build();
3799 assert_eq!(relation.to_string(), "debhelper");
3800 }
3801
3802 #[test]
3803 fn test_relation_builder_with_architectures() {
3804 let relation = Relation::build("samba")
3806 .architectures(vec!["amd64".to_string(), "i386".to_string()])
3807 .build();
3808 assert_eq!(relation.to_string(), "samba [amd64 i386]");
3809 }
3810
3811 #[test]
3812 fn test_ensure_minimum_version_add_new() {
3813 let mut relations: Relations = "python3".parse().unwrap();
3814 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3815 assert_eq!(relations.to_string(), "python3, debhelper (>= 12)");
3816 }
3817
3818 #[test]
3819 fn test_ensure_minimum_version_update() {
3820 let mut relations: Relations = "debhelper (>= 9)".parse().unwrap();
3821 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3822 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3823 }
3824
3825 #[test]
3826 fn test_ensure_minimum_version_no_change() {
3827 let mut relations: Relations = "debhelper (>= 13)".parse().unwrap();
3828 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3829 assert_eq!(relations.to_string(), "debhelper (>= 13)");
3830 }
3831
3832 #[test]
3833 fn test_ensure_minimum_version_no_version() {
3834 let mut relations: Relations = "debhelper".parse().unwrap();
3835 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3836 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3837 }
3838
3839 #[test]
3840 fn test_ensure_minimum_version_preserves_newline() {
3841 let input = "\n debhelper (>= 9),\n pkg-config,\n uuid-dev";
3847 let mut relations: Relations = input.parse().unwrap();
3848 relations.ensure_minimum_version("debhelper", &"12~".parse().unwrap());
3849 let result = relations.to_string();
3850
3851 assert!(
3853 result.starts_with('\n'),
3854 "Expected result to start with newline, got: {:?}",
3855 result
3856 );
3857 assert_eq!(result, "\n debhelper (>= 12~),\n pkg-config,\n uuid-dev");
3858 }
3859
3860 #[test]
3861 fn test_ensure_minimum_version_preserves_newline_in_control() {
3862 use crate::lossless::Control;
3864 use std::str::FromStr;
3865
3866 let input = r#"Source: f2fs-tools
3867Section: admin
3868Priority: optional
3869Maintainer: Test <test@example.com>
3870Build-Depends:
3871 debhelper (>= 9),
3872 pkg-config,
3873 uuid-dev
3874
3875Package: f2fs-tools
3876Description: test
3877"#;
3878
3879 let control = Control::from_str(input).unwrap();
3880 let mut source = control.source().unwrap();
3881 let mut build_depends = source.build_depends().unwrap();
3882
3883 let version = Version::from_str("12~").unwrap();
3884 build_depends.ensure_minimum_version("debhelper", &version);
3885
3886 source.set_build_depends(&build_depends);
3887
3888 let output = control.to_string();
3889
3890 assert!(
3892 output.contains("Build-Depends:\n debhelper (>= 12~)"),
3893 "Expected 'Build-Depends:\\n debhelper (>= 12~)' but got:\n{}",
3894 output
3895 );
3896 }
3897
3898 #[test]
3899 fn test_ensure_exact_version_add_new() {
3900 let mut relations: Relations = "python3".parse().unwrap();
3901 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3902 assert_eq!(relations.to_string(), "python3, debhelper (= 12)");
3903 }
3904
3905 #[test]
3906 fn test_ensure_exact_version_update() {
3907 let mut relations: Relations = "debhelper (>= 9)".parse().unwrap();
3908 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3909 assert_eq!(relations.to_string(), "debhelper (= 12)");
3910 }
3911
3912 #[test]
3913 fn test_ensure_exact_version_no_change() {
3914 let mut relations: Relations = "debhelper (= 12)".parse().unwrap();
3915 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3916 assert_eq!(relations.to_string(), "debhelper (= 12)");
3917 }
3918
3919 #[test]
3920 fn test_ensure_some_version_add_new() {
3921 let mut relations: Relations = "python3".parse().unwrap();
3922 relations.ensure_some_version("debhelper");
3923 assert_eq!(relations.to_string(), "python3, debhelper");
3924 }
3925
3926 #[test]
3927 fn test_ensure_some_version_exists_with_version() {
3928 let mut relations: Relations = "debhelper (>= 12)".parse().unwrap();
3929 relations.ensure_some_version("debhelper");
3930 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3931 }
3932
3933 #[test]
3934 fn test_ensure_some_version_exists_no_version() {
3935 let mut relations: Relations = "debhelper".parse().unwrap();
3936 relations.ensure_some_version("debhelper");
3937 assert_eq!(relations.to_string(), "debhelper");
3938 }
3939
3940 #[test]
3941 fn test_ensure_substvar() {
3942 let mut relations: Relations = "python3".parse().unwrap();
3943 relations.ensure_substvar("${misc:Depends}").unwrap();
3944 assert_eq!(relations.to_string(), "python3, ${misc:Depends}");
3945 }
3946
3947 #[test]
3948 fn test_ensure_substvar_already_exists() {
3949 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
3950 relations.ensure_substvar("${misc:Depends}").unwrap();
3951 assert_eq!(relations.to_string(), "python3, ${misc:Depends}");
3952 }
3953
3954 #[test]
3955 fn test_ensure_substvar_empty_relations() {
3956 let mut relations: Relations = Relations::new();
3957 relations.ensure_substvar("${misc:Depends}").unwrap();
3958 assert_eq!(relations.to_string(), "${misc:Depends}");
3959 }
3960
3961 #[test]
3962 fn test_ensure_substvar_preserves_whitespace() {
3963 let (mut relations, _) = Relations::parse_relaxed("python3, rustc", false);
3965 relations.ensure_substvar("${misc:Depends}").unwrap();
3966 assert_eq!(relations.to_string(), "python3, rustc, ${misc:Depends}");
3968 }
3969
3970 #[test]
3971 fn test_ensure_substvar_to_existing_substvar() {
3972 let (mut relations, _) = Relations::parse_relaxed("${shlibs:Depends}", true);
3975 relations.ensure_substvar("${misc:Depends}").unwrap();
3976 assert_eq!(relations.to_string(), "${shlibs:Depends}, ${misc:Depends}");
3978 }
3979
3980 #[test]
3981 fn test_drop_substvar_basic() {
3982 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
3983 relations.drop_substvar("${misc:Depends}");
3984 assert_eq!(relations.to_string(), "python3");
3985 }
3986
3987 #[test]
3988 fn test_drop_substvar_first_position() {
3989 let (mut relations, _) = Relations::parse_relaxed("${misc:Depends}, python3", true);
3990 relations.drop_substvar("${misc:Depends}");
3991 assert_eq!(relations.to_string(), "python3");
3992 }
3993
3994 #[test]
3995 fn test_drop_substvar_middle_position() {
3996 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}, rustc", true);
3997 relations.drop_substvar("${misc:Depends}");
3998 assert_eq!(relations.to_string(), "python3, rustc");
3999 }
4000
4001 #[test]
4002 fn test_drop_substvar_only_substvar() {
4003 let (mut relations, _) = Relations::parse_relaxed("${misc:Depends}", true);
4004 relations.drop_substvar("${misc:Depends}");
4005 assert_eq!(relations.to_string(), "");
4006 }
4007
4008 #[test]
4009 fn test_drop_substvar_not_exists() {
4010 let (mut relations, _) = Relations::parse_relaxed("python3, rustc", false);
4011 relations.drop_substvar("${misc:Depends}");
4012 assert_eq!(relations.to_string(), "python3, rustc");
4013 }
4014
4015 #[test]
4016 fn test_drop_substvar_multiple_substvars() {
4017 let (mut relations, _) =
4018 Relations::parse_relaxed("python3, ${misc:Depends}, ${shlibs:Depends}", true);
4019 relations.drop_substvar("${misc:Depends}");
4020 assert_eq!(relations.to_string(), "python3, ${shlibs:Depends}");
4021 }
4022
4023 #[test]
4024 fn test_drop_substvar_preserves_whitespace() {
4025 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
4026 relations.drop_substvar("${misc:Depends}");
4027 assert_eq!(relations.to_string(), "python3");
4028 }
4029
4030 #[test]
4031 fn test_filter_entries_basic() {
4032 let mut relations: Relations = "python3, debhelper, rustc".parse().unwrap();
4033 relations.filter_entries(|entry| entry.relations().any(|r| r.name().starts_with("python")));
4034 assert_eq!(relations.to_string(), "python3");
4035 }
4036
4037 #[test]
4038 fn test_filter_entries_keep_all() {
4039 let mut relations: Relations = "python3, debhelper".parse().unwrap();
4040 relations.filter_entries(|_| true);
4041 assert_eq!(relations.to_string(), "python3, debhelper");
4042 }
4043
4044 #[test]
4045 fn test_filter_entries_remove_all() {
4046 let mut relations: Relations = "python3, debhelper".parse().unwrap();
4047 relations.filter_entries(|_| false);
4048 assert_eq!(relations.to_string(), "");
4049 }
4050
4051 #[test]
4052 fn test_filter_entries_keep_middle() {
4053 let mut relations: Relations = "aaa, bbb, ccc".parse().unwrap();
4054 relations.filter_entries(|entry| entry.relations().any(|r| r.name() == "bbb"));
4055 assert_eq!(relations.to_string(), "bbb");
4056 }
4057
4058 #[test]
4061 fn test_is_sorted_wrap_and_sort_order() {
4062 let relations: Relations = "debhelper, python3, rustc".parse().unwrap();
4064 assert!(relations.is_sorted(&WrapAndSortOrder));
4065
4066 let relations: Relations = "rustc, debhelper, python3".parse().unwrap();
4068 assert!(!relations.is_sorted(&WrapAndSortOrder));
4069
4070 let (relations, _) =
4072 Relations::parse_relaxed("cdbs, debhelper-compat, python3, ${misc:Depends}", true);
4073 assert!(relations.is_sorted(&WrapAndSortOrder));
4074 }
4075
4076 #[test]
4077 fn test_is_sorted_default_order() {
4078 let relations: Relations = "aaa, bbb, ccc".parse().unwrap();
4080 assert!(relations.is_sorted(&DefaultSortingOrder));
4081
4082 let relations: Relations = "ccc, aaa, bbb".parse().unwrap();
4084 assert!(!relations.is_sorted(&DefaultSortingOrder));
4085
4086 let (relations, _) = Relations::parse_relaxed("aaa, bbb, ${misc:Depends}", true);
4088 assert!(relations.is_sorted(&DefaultSortingOrder));
4089 }
4090
4091 #[test]
4092 fn test_is_sorted_with_substvars() {
4093 let (relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}, rustc", true);
4095 assert!(relations.is_sorted(&DefaultSortingOrder));
4097 }
4098
4099 #[test]
4100 fn test_drop_dependency_exists() {
4101 let mut relations: Relations = "python3, debhelper, rustc".parse().unwrap();
4102 assert!(relations.drop_dependency("debhelper"));
4103 assert_eq!(relations.to_string(), "python3, rustc");
4104 }
4105
4106 #[test]
4107 fn test_drop_dependency_not_exists() {
4108 let mut relations: Relations = "python3, rustc".parse().unwrap();
4109 assert!(!relations.drop_dependency("nonexistent"));
4110 assert_eq!(relations.to_string(), "python3, rustc");
4111 }
4112
4113 #[test]
4114 fn test_drop_dependency_only_item() {
4115 let mut relations: Relations = "python3".parse().unwrap();
4116 assert!(relations.drop_dependency("python3"));
4117 assert_eq!(relations.to_string(), "");
4118 }
4119
4120 #[test]
4121 fn test_add_dependency_to_empty() {
4122 let mut relations: Relations = "".parse().unwrap();
4123 let entry = Entry::from(Relation::simple("python3"));
4124 relations.add_dependency(entry, None);
4125 assert_eq!(relations.to_string(), "python3");
4126 }
4127
4128 #[test]
4129 fn test_add_dependency_sorted_position() {
4130 let mut relations: Relations = "debhelper, rustc".parse().unwrap();
4131 let entry = Entry::from(Relation::simple("python3"));
4132 relations.add_dependency(entry, None);
4133 assert_eq!(relations.to_string(), "debhelper, python3, rustc");
4135 }
4136
4137 #[test]
4138 fn test_add_dependency_explicit_position() {
4139 let mut relations: Relations = "python3, rustc".parse().unwrap();
4140 let entry = Entry::from(Relation::simple("debhelper"));
4141 relations.add_dependency(entry, Some(0));
4142 assert_eq!(relations.to_string(), "debhelper, python3, rustc");
4143 }
4144
4145 #[test]
4146 fn test_add_dependency_build_system_first() {
4147 let mut relations: Relations = "python3, rustc".parse().unwrap();
4148 let entry = Entry::from(Relation::simple("debhelper-compat"));
4149 relations.add_dependency(entry, None);
4150 assert_eq!(relations.to_string(), "debhelper-compat, python3, rustc");
4152 }
4153
4154 #[test]
4155 fn test_add_dependency_at_end() {
4156 let mut relations: Relations = "debhelper, python3".parse().unwrap();
4157 let entry = Entry::from(Relation::simple("zzz-package"));
4158 relations.add_dependency(entry, None);
4159 assert_eq!(relations.to_string(), "debhelper, python3, zzz-package");
4161 }
4162
4163 #[test]
4164 fn test_add_dependency_to_single_entry() {
4165 let mut relations: Relations = "python3-dulwich".parse().unwrap();
4167 let entry: Entry = "debhelper-compat (= 12)".parse().unwrap();
4168 relations.add_dependency(entry, None);
4169 assert_eq!(
4171 relations.to_string(),
4172 "debhelper-compat (= 12), python3-dulwich"
4173 );
4174 }
4175
4176 #[test]
4177 fn test_get_relation_exists() {
4178 let relations: Relations = "python3, debhelper (>= 12), rustc".parse().unwrap();
4179 let result = relations.get_relation("debhelper");
4180 assert!(result.is_ok());
4181 let (idx, entry) = result.unwrap();
4182 assert_eq!(idx, 1);
4183 assert_eq!(entry.to_string(), "debhelper (>= 12)");
4184 }
4185
4186 #[test]
4187 fn test_get_relation_not_exists() {
4188 let relations: Relations = "python3, rustc".parse().unwrap();
4189 let result = relations.get_relation("nonexistent");
4190 assert_eq!(result, Err("Package nonexistent not found".to_string()));
4191 }
4192
4193 #[test]
4194 fn test_get_relation_complex_rule() {
4195 let relations: Relations = "python3 | python3-minimal, rustc".parse().unwrap();
4196 let result = relations.get_relation("python3");
4197 assert_eq!(
4198 result,
4199 Err("Complex rule for python3, aborting".to_string())
4200 );
4201 }
4202
4203 #[test]
4204 fn test_iter_relations_for_simple() {
4205 let relations: Relations = "python3, debhelper, python3-dev".parse().unwrap();
4206 let entries: Vec<_> = relations.iter_relations_for("python3").collect();
4207 assert_eq!(entries.len(), 1);
4208 assert_eq!(entries[0].0, 0);
4209 assert_eq!(entries[0].1.to_string(), "python3");
4210 }
4211
4212 #[test]
4213 fn test_iter_relations_for_alternatives() {
4214 let relations: Relations = "python3 | python3-minimal, python3-dev".parse().unwrap();
4215 let entries: Vec<_> = relations.iter_relations_for("python3").collect();
4216 assert_eq!(entries.len(), 1);
4218 assert_eq!(entries[0].0, 0);
4219 }
4220
4221 #[test]
4222 fn test_iter_relations_for_not_found() {
4223 let relations: Relations = "python3, rustc".parse().unwrap();
4224 let entries: Vec<_> = relations.iter_relations_for("debhelper").collect();
4225 assert_eq!(entries.len(), 0);
4226 }
4227
4228 #[test]
4229 fn test_has_relation_exists() {
4230 let relations: Relations = "python3, debhelper, rustc".parse().unwrap();
4231 assert!(relations.has_relation("debhelper"));
4232 assert!(relations.has_relation("python3"));
4233 assert!(relations.has_relation("rustc"));
4234 }
4235
4236 #[test]
4237 fn test_has_relation_not_exists() {
4238 let relations: Relations = "python3, rustc".parse().unwrap();
4239 assert!(!relations.has_relation("debhelper"));
4240 }
4241
4242 #[test]
4243 fn test_has_relation_in_alternative() {
4244 let relations: Relations = "python3 | python3-minimal".parse().unwrap();
4245 assert!(relations.has_relation("python3"));
4246 assert!(relations.has_relation("python3-minimal"));
4247 }
4248
4249 #[test]
4250 fn test_sorting_order_wrap_and_sort_build_systems() {
4251 let order = WrapAndSortOrder;
4252 assert!(order.lt("debhelper", "python3"));
4254 assert!(order.lt("debhelper-compat", "rustc"));
4255 assert!(order.lt("cdbs", "aaa"));
4256 assert!(order.lt("dh-python", "python3"));
4257 }
4258
4259 #[test]
4260 fn test_sorting_order_wrap_and_sort_regular_packages() {
4261 let order = WrapAndSortOrder;
4262 assert!(order.lt("aaa", "bbb"));
4264 assert!(order.lt("python3", "rustc"));
4265 assert!(!order.lt("rustc", "python3"));
4266 }
4267
4268 #[test]
4269 fn test_sorting_order_wrap_and_sort_substvars() {
4270 let order = WrapAndSortOrder;
4271 assert!(order.lt("python3", "${misc:Depends}"));
4273 assert!(!order.lt("${misc:Depends}", "python3"));
4274 assert!(!order.ignore("${misc:Depends}"));
4276 }
4277
4278 #[test]
4279 fn test_sorting_order_default_special_items() {
4280 let order = DefaultSortingOrder;
4281 assert!(order.lt("python3", "${misc:Depends}"));
4283 assert!(order.lt("aaa", "@cdbs@"));
4284 assert!(order.ignore("${misc:Depends}"));
4286 assert!(order.ignore("@cdbs@"));
4287 assert!(!order.ignore("python3"));
4288 }
4289
4290 #[test]
4291 fn test_is_special_package_name() {
4292 assert!(is_special_package_name("${misc:Depends}"));
4293 assert!(is_special_package_name("${shlibs:Depends}"));
4294 assert!(is_special_package_name("@cdbs@"));
4295 assert!(!is_special_package_name("python3"));
4296 assert!(!is_special_package_name("debhelper"));
4297 }
4298
4299 #[test]
4300 fn test_add_dependency_with_explicit_position() {
4301 let mut relations: Relations = "python3, rustc".parse().unwrap();
4303 let entry = Entry::from(Relation::simple("debhelper"));
4304 relations.add_dependency(entry, Some(1));
4305 assert_eq!(relations.to_string(), "python3, debhelper, rustc");
4307 }
4308
4309 #[test]
4310 fn test_whitespace_detection_single_space() {
4311 let mut relations: Relations = "python3, rustc".parse().unwrap();
4312 let entry = Entry::from(Relation::simple("debhelper"));
4313 relations.add_dependency(entry, Some(1));
4314 assert_eq!(relations.to_string(), "python3, debhelper, rustc");
4315 }
4316
4317 #[test]
4318 fn test_whitespace_detection_multiple_spaces() {
4319 let mut relations: Relations = "python3, rustc, gcc".parse().unwrap();
4320 let entry = Entry::from(Relation::simple("debhelper"));
4321 relations.add_dependency(entry, Some(1));
4322 assert_eq!(relations.to_string(), "python3, debhelper, rustc, gcc");
4324 }
4325
4326 #[test]
4327 fn test_whitespace_detection_mixed_patterns() {
4328 let mut relations: Relations = "a, b, c, d, e".parse().unwrap();
4330 let entry = Entry::from(Relation::simple("x"));
4331 relations.push(entry);
4332 assert_eq!(relations.to_string(), "a, b, c, d, e, x");
4335 }
4336
4337 #[test]
4338 fn test_whitespace_detection_newlines() {
4339 let mut relations: Relations = "python3,\n rustc".parse().unwrap();
4340 let entry = Entry::from(Relation::simple("debhelper"));
4341 relations.add_dependency(entry, Some(1));
4342 assert_eq!(relations.to_string(), "python3,\n debhelper,\n rustc");
4344 }
4345
4346 #[test]
4347 fn test_append_with_newline_no_trailing() {
4348 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4349 let entry = Entry::from(Relation::simple("blah"));
4350 relations.add_dependency(entry, None);
4351 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4352 }
4353
4354 #[test]
4355 fn test_append_with_trailing_newline() {
4356 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4357 let entry = Entry::from(Relation::simple("blah"));
4358 relations.add_dependency(entry, None);
4359 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4360 }
4361
4362 #[test]
4363 fn test_append_with_4_space_indent() {
4364 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4365 let entry = Entry::from(Relation::simple("blah"));
4366 relations.add_dependency(entry, None);
4367 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4368 }
4369
4370 #[test]
4371 fn test_append_with_4_space_and_trailing_newline() {
4372 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4373 let entry = Entry::from(Relation::simple("blah"));
4374 relations.add_dependency(entry, None);
4375 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4376 }
4377
4378 #[test]
4379 fn test_odd_syntax_append_no_trailing() {
4380 let mut relations: Relations = "\n foo\n , bar".parse().unwrap();
4381 let entry = Entry::from(Relation::simple("blah"));
4382 relations.add_dependency(entry, None);
4383 assert_eq!(relations.to_string(), "\n foo\n , bar\n , blah");
4384 }
4385
4386 #[test]
4387 fn test_odd_syntax_append_with_trailing() {
4388 let mut relations: Relations = "\n foo\n , bar\n".parse().unwrap();
4389 let entry = Entry::from(Relation::simple("blah"));
4390 relations.add_dependency(entry, None);
4391 assert_eq!(relations.to_string(), "\n foo\n , bar\n , blah");
4392 }
4393
4394 #[test]
4395 fn test_insert_at_1_no_trailing() {
4396 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4397 let entry = Entry::from(Relation::simple("blah"));
4398 relations.add_dependency(entry, Some(1));
4399 assert_eq!(relations.to_string(), "foo,\n blah,\n bar");
4400 }
4401
4402 #[test]
4403 fn test_insert_at_1_with_trailing() {
4404 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4405 let entry = Entry::from(Relation::simple("blah"));
4406 relations.add_dependency(entry, Some(1));
4407 assert_eq!(relations.to_string(), "foo,\n blah,\n bar");
4408 }
4409
4410 #[test]
4411 fn test_odd_syntax_insert_at_1() {
4412 let mut relations: Relations = "\n foo\n , bar\n".parse().unwrap();
4413 let entry = Entry::from(Relation::simple("blah"));
4414 relations.add_dependency(entry, Some(1));
4415 assert_eq!(relations.to_string(), "\n foo\n , blah\n , bar");
4416 }
4417
4418 #[test]
4419 fn test_relations_preserves_exact_whitespace() {
4420 let input =
4422 "debhelper (>= 10), quilt (>= 0.40),\n libsystemd-dev [linux-any], pkg-config";
4423
4424 let relations: Relations = input.parse().unwrap();
4425
4426 assert_eq!(
4428 relations.to_string(),
4429 input,
4430 "Relations should preserve exact whitespace from input"
4431 );
4432 }
4433
4434 #[test]
4435 fn test_remove_entry_preserves_indentation() {
4436 let input = "debhelper (>= 10), quilt (>= 0.40),\n libsystemd-dev [linux-any], dh-systemd (>= 1.5), pkg-config";
4438
4439 let mut relations: Relations = input.parse().unwrap();
4440
4441 let mut to_remove = Vec::new();
4443 for (idx, entry) in relations.entries().enumerate() {
4444 for relation in entry.relations() {
4445 if relation.name() == "dh-systemd" {
4446 to_remove.push(idx);
4447 break;
4448 }
4449 }
4450 }
4451
4452 for idx in to_remove.into_iter().rev() {
4453 relations.remove_entry(idx);
4454 }
4455
4456 let output = relations.to_string();
4457 println!("After removal: '{}'", output);
4458
4459 assert!(
4461 output.contains("\n libsystemd-dev"),
4462 "Expected 4-space indentation to be preserved, but got:\n'{}'",
4463 output
4464 );
4465 }
4466
4467 #[test]
4468 fn test_relation_is_implied_by_same_package() {
4469 let inner = Relation::new(
4471 "pkg",
4472 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4473 );
4474 let outer = Relation::new(
4475 "pkg",
4476 Some((VersionConstraint::GreaterThanEqual, "1.5".parse().unwrap())),
4477 );
4478 assert!(inner.is_implied_by(&outer));
4479 }
4480
4481 #[test]
4482 fn test_relation_is_implied_by_different_package() {
4483 let inner = Relation::new("pkg1", None);
4485 let outer = Relation::new("pkg2", None);
4486 assert!(!inner.is_implied_by(&outer));
4487 }
4488
4489 #[test]
4490 fn test_relation_is_implied_by_no_version() {
4491 let inner = Relation::new("pkg", None);
4493 let outer = Relation::new(
4494 "pkg",
4495 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4496 );
4497 assert!(inner.is_implied_by(&outer));
4498 }
4499
4500 #[test]
4501 fn test_relation_is_implied_by_identical() {
4502 let inner = Relation::new(
4504 "pkg",
4505 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4506 );
4507 let outer = Relation::new(
4508 "pkg",
4509 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4510 );
4511 assert!(inner.is_implied_by(&outer));
4512 assert!(outer.is_implied_by(&inner));
4513 }
4514
4515 #[test]
4516 fn test_relation_is_implied_by_greater_than_equal() {
4517 let inner = Relation::new(
4519 "pkg",
4520 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4521 );
4522 let outer = Relation::new(
4523 "pkg",
4524 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
4525 );
4526 assert!(inner.is_implied_by(&outer));
4527 assert!(!outer.is_implied_by(&inner));
4528
4529 let outer = Relation::new(
4531 "pkg",
4532 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4533 );
4534 assert!(inner.is_implied_by(&outer));
4535
4536 let outer = Relation::new(
4538 "pkg",
4539 Some((VersionConstraint::GreaterThan, "1.5".parse().unwrap())),
4540 );
4541 assert!(inner.is_implied_by(&outer));
4542
4543 let inner = Relation::new(
4545 "pkg",
4546 Some((VersionConstraint::GreaterThanEqual, "3.0".parse().unwrap())),
4547 );
4548 let outer = Relation::new(
4549 "pkg",
4550 Some((VersionConstraint::GreaterThan, "3.0".parse().unwrap())),
4551 );
4552 assert!(!inner.is_implied_by(&outer));
4553 }
4554
4555 #[test]
4556 fn test_relation_is_implied_by_less_than_equal() {
4557 let inner = Relation::new(
4559 "pkg",
4560 Some((VersionConstraint::LessThanEqual, "2.0".parse().unwrap())),
4561 );
4562 let outer = Relation::new(
4563 "pkg",
4564 Some((VersionConstraint::LessThanEqual, "1.0".parse().unwrap())),
4565 );
4566 assert!(inner.is_implied_by(&outer));
4567 assert!(!outer.is_implied_by(&inner));
4568
4569 let outer = Relation::new(
4571 "pkg",
4572 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4573 );
4574 assert!(inner.is_implied_by(&outer));
4575
4576 let outer = Relation::new(
4578 "pkg",
4579 Some((VersionConstraint::LessThan, "1.5".parse().unwrap())),
4580 );
4581 assert!(inner.is_implied_by(&outer));
4582 }
4583
4584 #[test]
4585 fn test_relation_is_implied_by_equal() {
4586 let inner = Relation::new(
4588 "pkg",
4589 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4590 );
4591 let outer = Relation::new(
4592 "pkg",
4593 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4594 );
4595 assert!(inner.is_implied_by(&outer));
4596
4597 let outer = Relation::new(
4599 "pkg",
4600 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4601 );
4602 assert!(!inner.is_implied_by(&outer));
4603
4604 let outer = Relation::new(
4606 "pkg",
4607 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4608 );
4609 assert!(!inner.is_implied_by(&outer));
4610 }
4611
4612 #[test]
4613 fn test_relation_is_implied_by_greater_than() {
4614 let inner = Relation::new(
4616 "pkg",
4617 Some((VersionConstraint::GreaterThan, "1.0".parse().unwrap())),
4618 );
4619 let outer = Relation::new(
4620 "pkg",
4621 Some((VersionConstraint::GreaterThan, "2.0".parse().unwrap())),
4622 );
4623 assert!(inner.is_implied_by(&outer));
4624
4625 let outer = Relation::new(
4627 "pkg",
4628 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4629 );
4630 assert!(inner.is_implied_by(&outer));
4631
4632 let outer = Relation::new(
4634 "pkg",
4635 Some((VersionConstraint::GreaterThanEqual, "1.5".parse().unwrap())),
4636 );
4637 assert!(inner.is_implied_by(&outer));
4638
4639 let outer = Relation::new(
4641 "pkg",
4642 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4643 );
4644 assert!(!inner.is_implied_by(&outer));
4645 }
4646
4647 #[test]
4648 fn test_relation_is_implied_by_less_than() {
4649 let inner = Relation::new(
4651 "pkg",
4652 Some((VersionConstraint::LessThan, "2.0".parse().unwrap())),
4653 );
4654 let outer = Relation::new(
4655 "pkg",
4656 Some((VersionConstraint::LessThan, "1.0".parse().unwrap())),
4657 );
4658 assert!(inner.is_implied_by(&outer));
4659
4660 let outer = Relation::new(
4662 "pkg",
4663 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4664 );
4665 assert!(inner.is_implied_by(&outer));
4666
4667 let outer = Relation::new(
4669 "pkg",
4670 Some((VersionConstraint::LessThanEqual, "1.5".parse().unwrap())),
4671 );
4672 assert!(inner.is_implied_by(&outer));
4673 }
4674
4675 #[test]
4676 fn test_relation_is_implied_by_incompatible_constraints() {
4677 let inner = Relation::new(
4679 "pkg",
4680 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4681 );
4682 let outer = Relation::new(
4683 "pkg",
4684 Some((VersionConstraint::LessThanEqual, "2.0".parse().unwrap())),
4685 );
4686 assert!(!inner.is_implied_by(&outer));
4687 assert!(!outer.is_implied_by(&inner));
4688 }
4689
4690 #[test]
4691 fn test_entry_is_implied_by_identical() {
4692 let inner: Entry = "pkg (>= 1.0)".parse().unwrap();
4693 let outer: Entry = "pkg (>= 1.0)".parse().unwrap();
4694 assert!(inner.is_implied_by(&outer));
4695 }
4696
4697 #[test]
4698 fn test_entry_is_implied_by_or_group() {
4699 let inner: Entry = "pkg (>= 1.0)".parse().unwrap();
4701 let outer: Entry = "pkg (>= 1.5) | libc6".parse().unwrap();
4702 assert!(inner.is_implied_by(&outer));
4703 }
4704
4705 #[test]
4706 fn test_entry_is_implied_by_simple_or() {
4707 let inner: Entry = "pkg1 | pkg2".parse().unwrap();
4709 let outer: Entry = "pkg1".parse().unwrap();
4710 assert!(inner.is_implied_by(&outer));
4711
4712 let outer: Entry = "pkg2".parse().unwrap();
4714 assert!(inner.is_implied_by(&outer));
4715 }
4716
4717 #[test]
4718 fn test_entry_is_implied_by_not_implied() {
4719 let inner: Entry = "pkg (>= 2.0)".parse().unwrap();
4721 let outer: Entry = "pkg (>= 1.0)".parse().unwrap();
4722 assert!(!inner.is_implied_by(&outer));
4723 }
4724
4725 #[test]
4726 fn test_entry_is_implied_by_different_packages() {
4727 let inner: Entry = "pkg1".parse().unwrap();
4728 let outer: Entry = "pkg2".parse().unwrap();
4729 assert!(!inner.is_implied_by(&outer));
4730 }
4731
4732 #[test]
4733 fn test_entry_is_implied_by_complex_or() {
4734 let inner: Entry = "pkg1 | pkg2".parse().unwrap();
4736 let outer: Entry = "pkg1 | pkg2".parse().unwrap();
4737 assert!(inner.is_implied_by(&outer));
4738
4739 let outer: Entry = "pkg1 | pkg2 | pkg3".parse().unwrap();
4741 assert!(inner.is_implied_by(&outer));
4742 }
4743
4744 #[test]
4745 fn test_parse_version_with_epoch() {
4746 let input = "amule-dbg (<< 1:2.3.2-2~)";
4749 let parsed: Relations = input.parse().unwrap();
4750 assert_eq!(parsed.to_string(), input);
4751 assert_eq!(parsed.entries().count(), 1);
4752 let entry = parsed.entries().next().unwrap();
4753 assert_eq!(entry.to_string(), "amule-dbg (<< 1:2.3.2-2~)");
4754 assert_eq!(entry.relations().count(), 1);
4755 let relation = entry.relations().next().unwrap();
4756 assert_eq!(relation.name(), "amule-dbg");
4757 assert_eq!(relation.to_string(), "amule-dbg (<< 1:2.3.2-2~)");
4758 assert_eq!(
4759 relation.version(),
4760 Some((VersionConstraint::LessThan, "1:2.3.2-2~".parse().unwrap()))
4761 );
4762 }
4763
4764 #[test]
4765 fn test_ensure_relation_add_new() {
4766 let mut relations: Relations = "python3".parse().unwrap();
4768 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4769 let added = relations.ensure_relation(new_entry);
4770 assert!(added);
4771 assert_eq!(relations.to_string(), "debhelper (>= 12), python3");
4773 }
4774
4775 #[test]
4776 fn test_ensure_relation_already_satisfied() {
4777 let mut relations: Relations = "debhelper (>= 13)".parse().unwrap();
4779 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4780 let added = relations.ensure_relation(new_entry);
4781 assert!(!added);
4782 assert_eq!(relations.to_string(), "debhelper (>= 13)");
4783 }
4784
4785 #[test]
4786 fn test_ensure_relation_replace_weaker() {
4787 let mut relations: Relations = "debhelper (>= 11)".parse().unwrap();
4789 let new_entry: Entry = "debhelper (>= 13)".parse().unwrap();
4790 let added = relations.ensure_relation(new_entry);
4791 assert!(added);
4792 assert_eq!(relations.to_string(), "debhelper (>= 13)");
4793 }
4794
4795 #[test]
4796 fn test_ensure_relation_replace_multiple_weaker() {
4797 let mut relations: Relations = "debhelper (>= 11), debhelper (>= 10), python3"
4799 .parse()
4800 .unwrap();
4801 let new_entry: Entry = "debhelper (>= 13)".parse().unwrap();
4802 let added = relations.ensure_relation(new_entry);
4803 assert!(added);
4804 assert_eq!(relations.to_string(), "debhelper (>= 13), python3");
4805 }
4806
4807 #[test]
4808 fn test_ensure_relation_identical_entry() {
4809 let mut relations: Relations = "debhelper (>= 12)".parse().unwrap();
4811 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4812 let added = relations.ensure_relation(new_entry);
4813 assert!(!added);
4814 assert_eq!(relations.to_string(), "debhelper (>= 12)");
4815 }
4816
4817 #[test]
4818 fn test_ensure_relation_no_version_constraint() {
4819 let mut relations: Relations = "python3".parse().unwrap();
4821 let new_entry: Entry = "debhelper".parse().unwrap();
4822 let added = relations.ensure_relation(new_entry);
4823 assert!(added);
4824 assert_eq!(relations.to_string(), "debhelper, python3");
4826 }
4827
4828 #[test]
4829 fn test_ensure_relation_strengthen_unversioned() {
4830 let mut relations: Relations = "debhelper".parse().unwrap();
4833 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4834 let added = relations.ensure_relation(new_entry);
4835 assert!(added);
4836 assert_eq!(relations.to_string(), "debhelper (>= 12)");
4837 }
4838
4839 #[test]
4840 fn test_ensure_relation_versioned_implies_unversioned() {
4841 let mut relations: Relations = "debhelper (>= 12)".parse().unwrap();
4844 let new_entry: Entry = "debhelper".parse().unwrap();
4845 let added = relations.ensure_relation(new_entry);
4846 assert!(!added);
4847 assert_eq!(relations.to_string(), "debhelper (>= 12)");
4848 }
4849
4850 #[test]
4851 fn test_ensure_relation_preserves_whitespace() {
4852 let mut relations: Relations = "python3, rustc".parse().unwrap();
4854 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4855 let added = relations.ensure_relation(new_entry);
4856 assert!(added);
4857 assert_eq!(relations.to_string(), "debhelper (>= 12), python3, rustc");
4859 }
4860
4861 #[test]
4862 fn test_ensure_relation_empty_relations() {
4863 let mut relations: Relations = Relations::new();
4865 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4866 let added = relations.ensure_relation(new_entry);
4867 assert!(added);
4868 assert_eq!(relations.to_string(), "debhelper (>= 12)");
4869 }
4870
4871 #[test]
4872 fn test_ensure_relation_alternative_dependencies() {
4873 let mut relations: Relations = "python3 | python3-minimal".parse().unwrap();
4875 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4876 let added = relations.ensure_relation(new_entry);
4877 assert!(added);
4878 assert_eq!(
4880 relations.to_string(),
4881 "debhelper (>= 12), python3 | python3-minimal"
4882 );
4883 }
4884
4885 #[test]
4886 fn test_ensure_relation_replace_in_middle() {
4887 let mut relations: Relations = "python3, debhelper (>= 11), rustc".parse().unwrap();
4889 let new_entry: Entry = "debhelper (>= 13)".parse().unwrap();
4890 let added = relations.ensure_relation(new_entry);
4891 assert!(added);
4892 assert_eq!(relations.to_string(), "python3, debhelper (>= 13), rustc");
4893 }
4894
4895 #[test]
4896 fn test_ensure_relation_with_different_package() {
4897 let mut relations: Relations = "python3, debhelper (>= 12)".parse().unwrap();
4899 let new_entry: Entry = "rustc".parse().unwrap();
4900 let added = relations.ensure_relation(new_entry);
4901 assert!(added);
4902 assert_eq!(relations.to_string(), "python3, debhelper (>= 12), rustc");
4903 }
4904}