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 return String::from(""); }
818
819 if num_entries == 1 {
820 if let Some(node) = entries[0].0.next_sibling_or_token() {
822 if node.kind() == COMMA {
823 let pattern = Self::collect_whitespace(node.next_sibling_or_token());
824 if !pattern.is_empty() {
825 return pattern;
826 }
827 }
828 }
829 return default.to_string(); }
831
832 let mut whitespace_counts: HashMap<String, usize> = HashMap::new();
834
835 for (i, entry) in entries.iter().enumerate() {
836 if i == num_entries - 1 {
837 break; }
839
840 if let Some(mut node) = entry.0.next_sibling_or_token() {
842 while matches!(node.kind(), WHITESPACE | NEWLINE) {
844 if let Some(next) = node.next_sibling_or_token() {
845 node = next;
846 } else {
847 break;
848 }
849 }
850
851 if node.kind() == COMMA {
853 let pattern = Self::collect_whitespace(node.next_sibling_or_token());
854 if !pattern.is_empty() {
855 *whitespace_counts.entry(pattern).or_insert(0) += 1;
856 }
857 }
858 }
859 }
860
861 if whitespace_counts.len() == 1 {
863 if let Some((ws, _)) = whitespace_counts.iter().next() {
864 return ws.clone();
865 }
866 }
867
868 if let Some((ws, _)) = whitespace_counts.iter().max_by_key(|(_, count)| *count) {
870 return ws.clone();
871 }
872
873 default.to_string()
875 }
876
877 pub fn insert_with_separator(&mut self, idx: usize, entry: Entry, default_sep: Option<&str>) {
884 let is_empty = self.entries().next().is_none();
885 let whitespace = self.detect_whitespace_pattern(default_sep.unwrap_or(" "));
886
887 self.strip_trailing_whitespace();
889
890 let odd_syntax = self.detect_odd_syntax();
892
893 let (position, new_children) = if let Some(current_entry) = self.entries().nth(idx) {
894 let to_insert = if idx == 0 && is_empty {
895 vec![entry.0.green().into()]
896 } else if let Some((before_ws, after_ws)) = &odd_syntax {
897 let mut nodes = vec![entry.0.green().into()];
898 nodes.extend(Self::build_odd_syntax_nodes(before_ws, after_ws));
899 nodes
900 } else {
901 vec![
902 entry.0.green().into(),
903 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
904 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), whitespace.as_str())),
905 ]
906 };
907
908 (current_entry.0.index(), to_insert)
909 } else {
910 let child_count = self.0.children_with_tokens().count();
911 let to_insert = if idx == 0 {
912 vec![entry.0.green().into()]
913 } else if let Some((before_ws, after_ws)) = &odd_syntax {
914 let mut nodes = Self::build_odd_syntax_nodes(before_ws, after_ws);
915 nodes.push(entry.0.green().into());
916 nodes
917 } else {
918 vec![
919 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
920 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), whitespace.as_str())),
921 entry.0.green().into(),
922 ]
923 };
924
925 (child_count, to_insert)
926 };
927 self.0 = SyntaxNode::new_root_mut(
929 self.0.replace_with(
930 self.0
931 .green()
932 .splice_children(position..position, new_children),
933 ),
934 );
935 }
936
937 pub fn insert(&mut self, idx: usize, entry: Entry) {
939 self.insert_with_separator(idx, entry, None);
940 }
941
942 fn strip_entry_trailing_ws(entry: &SyntaxNode) -> GreenNode {
944 let mut children: Vec<_> = entry
945 .children_with_tokens()
946 .map(|c| Self::to_green(&c))
947 .collect();
948
949 if let Some(NodeOrToken::Node(last)) = children.last() {
951 if last.kind() == rowan::SyntaxKind(RELATION as u16) {
952 let relation_node = entry.children().last().unwrap();
954 children.pop();
955 children.push(NodeOrToken::Node(Self::strip_relation_trailing_ws(
956 &relation_node,
957 )));
958 }
959 }
960
961 let stripped = Self::strip_trailing_ws_from_children(children);
963 GreenNode::new(ENTRY.into(), stripped)
964 }
965
966 fn strip_trailing_whitespace(&mut self) {
967 let mut children: Vec<_> = self
968 .0
969 .children_with_tokens()
970 .map(|c| Self::to_green(&c))
971 .collect();
972
973 if let Some(NodeOrToken::Node(last)) = children.last() {
975 if last.kind() == rowan::SyntaxKind(ENTRY as u16) {
976 let last_entry = self.0.children().last().unwrap();
977 children.pop();
978 children.push(NodeOrToken::Node(Self::strip_entry_trailing_ws(
979 &last_entry,
980 )));
981 }
982 }
983
984 let stripped = Self::strip_trailing_ws_from_children(children);
986
987 let nc = self.0.children_with_tokens().count();
988 self.0 = SyntaxNode::new_root_mut(
989 self.0
990 .replace_with(self.0.green().splice_children(0..nc, stripped)),
991 );
992 }
993
994 pub fn replace(&mut self, idx: usize, entry: Entry) {
996 let current_entry = self.get_entry(idx).unwrap();
997 self.0.splice_children(
998 current_entry.0.index()..current_entry.0.index() + 1,
999 vec![entry.0.into()],
1000 );
1001 }
1002
1003 pub fn push(&mut self, entry: Entry) {
1005 let pos = self.entries().count();
1006 self.insert(pos, entry);
1007 }
1008
1009 pub fn substvars(&self) -> impl Iterator<Item = String> + '_ {
1011 self.0
1012 .children()
1013 .filter_map(Substvar::cast)
1014 .map(|s| s.to_string())
1015 }
1016
1017 pub fn parse_relaxed(s: &str, allow_substvar: bool) -> (Relations, Vec<String>) {
1019 let parse = parse(s, allow_substvar);
1020 (parse.root_mut(), parse.errors)
1021 }
1022
1023 pub fn satisfied_by(&self, package_version: impl crate::VersionLookup + Copy) -> bool {
1025 self.entries().all(|e| e.satisfied_by(package_version))
1026 }
1027
1028 pub fn is_empty(&self) -> bool {
1030 self.entries().count() == 0
1031 }
1032
1033 pub fn len(&self) -> usize {
1035 self.entries().count()
1036 }
1037
1038 pub fn ensure_minimum_version(&mut self, package: &str, minimum_version: &Version) {
1061 let mut found = false;
1062 let mut obsolete_indices = vec![];
1063 let mut update_idx = None;
1064
1065 let entries: Vec<_> = self.entries().collect();
1066 for (idx, entry) in entries.iter().enumerate() {
1067 let relations: Vec<_> = entry.relations().collect();
1068
1069 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1071 if names.len() > 1 && names.contains(&package.to_string()) {
1072 let is_obsolete = relations.iter().any(|r| {
1074 if r.name() != package {
1075 return false;
1076 }
1077 if let Some((vc, ver)) = r.version() {
1078 matches!(vc, VersionConstraint::GreaterThan if &ver < minimum_version)
1079 || matches!(vc, VersionConstraint::GreaterThanEqual if &ver <= minimum_version)
1080 } else {
1081 false
1082 }
1083 });
1084 if is_obsolete {
1085 obsolete_indices.push(idx);
1086 }
1087 continue;
1088 }
1089
1090 if names.len() == 1 && names[0] == package {
1092 found = true;
1093 let relation = relations.into_iter().next().unwrap();
1094
1095 let should_update = if let Some((vc, ver)) = relation.version() {
1097 match vc {
1098 VersionConstraint::GreaterThanEqual | VersionConstraint::GreaterThan => {
1099 &ver < minimum_version
1100 }
1101 _ => false,
1102 }
1103 } else {
1104 true
1105 };
1106
1107 if should_update {
1108 update_idx = Some(idx);
1109 }
1110 break;
1111 }
1112 }
1113
1114 if let Some(idx) = update_idx {
1116 let relation = Relation::new(
1117 package,
1118 Some((VersionConstraint::GreaterThanEqual, minimum_version.clone())),
1119 );
1120 let mut entry = self.get_entry(idx).unwrap();
1122 entry.replace(0, relation);
1123 self.replace(idx, entry);
1124 }
1125
1126 for idx in obsolete_indices.into_iter().rev() {
1128 self.remove_entry(idx);
1129 }
1130
1131 if !found {
1133 let relation = Relation::new(
1134 package,
1135 Some((VersionConstraint::GreaterThanEqual, minimum_version.clone())),
1136 );
1137 self.push(Entry::from(relation));
1138 }
1139 }
1140
1141 pub fn ensure_exact_version(&mut self, package: &str, version: &Version) {
1156 let mut found = false;
1157 let mut update_idx = None;
1158
1159 let entries: Vec<_> = self.entries().collect();
1160 for (idx, entry) in entries.iter().enumerate() {
1161 let relations: Vec<_> = entry.relations().collect();
1162 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1163
1164 if names.len() > 1 && names[0] == package {
1165 panic!("Complex rule for {}, aborting", package);
1166 }
1167
1168 if names.len() == 1 && names[0] == package {
1169 found = true;
1170 let relation = relations.into_iter().next().unwrap();
1171
1172 let should_update = if let Some((vc, ver)) = relation.version() {
1173 vc != VersionConstraint::Equal || &ver != version
1174 } else {
1175 true
1176 };
1177
1178 if should_update {
1179 update_idx = Some(idx);
1180 }
1181 break;
1182 }
1183 }
1184
1185 if let Some(idx) = update_idx {
1187 let relation =
1188 Relation::new(package, Some((VersionConstraint::Equal, version.clone())));
1189 let mut entry = self.get_entry(idx).unwrap();
1191 entry.replace(0, relation);
1192 self.replace(idx, entry);
1193 }
1194
1195 if !found {
1196 let relation =
1197 Relation::new(package, Some((VersionConstraint::Equal, version.clone())));
1198 self.push(Entry::from(relation));
1199 }
1200 }
1201
1202 pub fn ensure_some_version(&mut self, package: &str) {
1220 for entry in self.entries() {
1221 let relations: Vec<_> = entry.relations().collect();
1222 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1223
1224 if names.len() > 1 && names[0] == package {
1225 panic!("Complex rule for {}, aborting", package);
1226 }
1227
1228 if names.len() == 1 && names[0] == package {
1229 return;
1231 }
1232 }
1233
1234 let relation = Relation::simple(package);
1236 self.push(Entry::from(relation));
1237 }
1238
1239 pub fn ensure_substvar(&mut self, substvar: &str) -> Result<(), String> {
1259 for existing in self.substvars() {
1261 if existing.trim() == substvar.trim() {
1262 return Ok(());
1263 }
1264 }
1265
1266 let (parsed, errors) = Relations::parse_relaxed(substvar, true);
1268 if !errors.is_empty() {
1269 return Err(errors.join("\n"));
1270 }
1271
1272 let whitespace = self.detect_whitespace_pattern(" ");
1274
1275 for substvar_node in parsed.0.children().filter(|n| n.kind() == SUBSTVAR) {
1277 let has_content = self.entries().next().is_some() || self.substvars().next().is_some();
1278
1279 let mut builder = GreenNodeBuilder::new();
1280 builder.start_node(ROOT.into());
1281
1282 for child in self.0.children_with_tokens() {
1284 match child {
1285 NodeOrToken::Node(n) => inject(&mut builder, n),
1286 NodeOrToken::Token(t) => builder.token(t.kind().into(), t.text()),
1287 }
1288 }
1289
1290 if has_content {
1292 builder.token(COMMA.into(), ",");
1293 builder.token(WHITESPACE.into(), whitespace.as_str());
1294 }
1295
1296 inject(&mut builder, substvar_node);
1298
1299 builder.finish_node();
1300 self.0 = SyntaxNode::new_root_mut(builder.finish());
1301 }
1302
1303 Ok(())
1304 }
1305
1306 pub fn drop_substvar(&mut self, substvar: &str) {
1323 let substvars_to_remove: Vec<_> = self
1325 .0
1326 .children()
1327 .filter_map(Substvar::cast)
1328 .filter(|s| s.to_string().trim() == substvar.trim())
1329 .collect();
1330
1331 for substvar_node in substvars_to_remove {
1332 let is_first = !substvar_node
1334 .0
1335 .siblings(Direction::Prev)
1336 .skip(1)
1337 .any(|n| n.kind() == ENTRY || n.kind() == SUBSTVAR);
1338
1339 let mut removed_comma = false;
1340
1341 while let Some(n) = substvar_node.0.next_sibling_or_token() {
1343 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1344 n.detach();
1345 } else if n.kind() == COMMA {
1346 n.detach();
1347 removed_comma = true;
1348 break;
1349 } else {
1350 break;
1351 }
1352 }
1353
1354 if !is_first {
1356 while let Some(n) = substvar_node.0.prev_sibling_or_token() {
1357 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1358 n.detach();
1359 } else if !removed_comma && n.kind() == COMMA {
1360 n.detach();
1361 break;
1362 } else {
1363 break;
1364 }
1365 }
1366 } else {
1367 while let Some(n) = substvar_node.0.next_sibling_or_token() {
1369 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1370 n.detach();
1371 } else {
1372 break;
1373 }
1374 }
1375 }
1376
1377 substvar_node.0.detach();
1379 }
1380 }
1381
1382 pub fn filter_entries<F>(&mut self, keep: F)
1398 where
1399 F: Fn(&Entry) -> bool,
1400 {
1401 let indices_to_remove: Vec<_> = self
1402 .entries()
1403 .enumerate()
1404 .filter_map(|(idx, entry)| if keep(&entry) { None } else { Some(idx) })
1405 .collect();
1406
1407 for idx in indices_to_remove.into_iter().rev() {
1409 self.remove_entry(idx);
1410 }
1411 }
1412
1413 pub fn is_sorted(&self, sorting_order: &impl SortingOrder) -> bool {
1429 let mut last_name: Option<String> = None;
1430 for entry in self.entries() {
1431 let mut relations = entry.relations();
1433 let Some(relation) = relations.next() else {
1434 continue;
1435 };
1436
1437 let name = relation.name();
1438
1439 if sorting_order.ignore(&name) {
1441 continue;
1442 }
1443
1444 if let Some(ref last) = last_name {
1446 if sorting_order.lt(&name, last) {
1447 return false;
1448 }
1449 }
1450
1451 last_name = Some(name);
1452 }
1453 true
1454 }
1455
1456 fn find_insert_position(&self, entry: &Entry) -> usize {
1468 let Some(relation) = entry.relations().next() else {
1470 return self.len();
1472 };
1473 let package_name = relation.name();
1474
1475 let count = self.entries().filter(|e| !e.is_empty()).count();
1477
1478 let sorting_order: Box<dyn SortingOrder> = if count < 2 {
1480 Box::new(WrapAndSortOrder)
1481 } else {
1482 if self.is_sorted(&WrapAndSortOrder) {
1485 Box::new(WrapAndSortOrder)
1486 } else if self.is_sorted(&DefaultSortingOrder) {
1487 Box::new(DefaultSortingOrder)
1488 } else {
1489 return self.len();
1491 }
1492 };
1493
1494 if sorting_order.ignore(&package_name) {
1496 return self.len();
1497 }
1498
1499 let mut position = 0;
1501 for (idx, existing_entry) in self.entries().enumerate() {
1502 let mut existing_relations = existing_entry.relations();
1503 let Some(existing_relation) = existing_relations.next() else {
1504 position += 1;
1506 continue;
1507 };
1508
1509 let existing_name = existing_relation.name();
1510
1511 if sorting_order.ignore(&existing_name) {
1513 position += 1;
1514 continue;
1515 }
1516
1517 if sorting_order.lt(&package_name, &existing_name) {
1519 return idx;
1520 }
1521 position += 1;
1522 }
1523
1524 position
1525 }
1526
1527 pub fn drop_dependency(&mut self, package: &str) -> bool {
1545 let indices_to_remove: Vec<_> = self
1546 .entries()
1547 .enumerate()
1548 .filter_map(|(idx, entry)| {
1549 let relations: Vec<_> = entry.relations().collect();
1550 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1551 if names == vec![package] {
1552 Some(idx)
1553 } else {
1554 None
1555 }
1556 })
1557 .collect();
1558
1559 let found = !indices_to_remove.is_empty();
1560
1561 for idx in indices_to_remove.into_iter().rev() {
1563 self.remove_entry(idx);
1564 }
1565
1566 found
1567 }
1568
1569 pub fn add_dependency(&mut self, entry: Entry, position: Option<usize>) {
1590 let pos = position.unwrap_or_else(|| self.find_insert_position(&entry));
1591 self.insert(pos, entry);
1592 }
1593
1594 pub fn get_relation(&self, package: &str) -> Result<(usize, Entry), String> {
1618 for (idx, entry) in self.entries().enumerate() {
1619 let relations: Vec<_> = entry.relations().collect();
1620 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1621
1622 if names.len() > 1 && names.contains(&package.to_string()) {
1623 return Err(format!("Complex rule for {}, aborting", package));
1624 }
1625
1626 if names.len() == 1 && names[0] == package {
1627 return Ok((idx, entry));
1628 }
1629 }
1630 Err(format!("Package {} not found", package))
1631 }
1632
1633 pub fn iter_relations_for(&self, package: &str) -> impl Iterator<Item = (usize, Entry)> + '_ {
1650 let package = package.to_string();
1651 self.entries().enumerate().filter(move |(_, entry)| {
1652 let names: Vec<_> = entry.relations().map(|r| r.name()).collect();
1653 names.contains(&package)
1654 })
1655 }
1656
1657 pub fn has_relation(&self, package: &str) -> bool {
1674 self.entries()
1675 .any(|entry| entry.relations().any(|r| r.name() == package))
1676 }
1677}
1678
1679impl From<Vec<Entry>> for Relations {
1680 fn from(entries: Vec<Entry>) -> Self {
1681 let mut builder = GreenNodeBuilder::new();
1682 builder.start_node(ROOT.into());
1683 for (i, entry) in entries.into_iter().enumerate() {
1684 if i > 0 {
1685 builder.token(COMMA.into(), ",");
1686 builder.token(WHITESPACE.into(), " ");
1687 }
1688 inject(&mut builder, entry.0);
1689 }
1690 builder.finish_node();
1691 Relations(SyntaxNode::new_root_mut(builder.finish()))
1692 }
1693}
1694
1695impl From<Entry> for Relations {
1696 fn from(entry: Entry) -> Self {
1697 Self::from(vec![entry])
1698 }
1699}
1700
1701impl Default for Entry {
1702 fn default() -> Self {
1703 Self::new()
1704 }
1705}
1706
1707impl PartialOrd for Entry {
1708 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1709 let mut rels_a = self.relations();
1710 let mut rels_b = other.relations();
1711 while let (Some(a), Some(b)) = (rels_a.next(), rels_b.next()) {
1712 match a.cmp(&b) {
1713 std::cmp::Ordering::Equal => continue,
1714 x => return Some(x),
1715 }
1716 }
1717
1718 if rels_a.next().is_some() {
1719 return Some(std::cmp::Ordering::Greater);
1720 }
1721
1722 if rels_b.next().is_some() {
1723 return Some(std::cmp::Ordering::Less);
1724 }
1725
1726 Some(std::cmp::Ordering::Equal)
1727 }
1728}
1729
1730impl Eq for Entry {}
1731
1732impl Ord for Entry {
1733 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1734 self.partial_cmp(other).unwrap()
1735 }
1736}
1737
1738impl Entry {
1739 pub fn new() -> Self {
1741 let mut builder = GreenNodeBuilder::new();
1742 builder.start_node(SyntaxKind::ENTRY.into());
1743 builder.finish_node();
1744 Entry(SyntaxNode::new_root_mut(builder.finish()))
1745 }
1746
1747 pub fn replace(&mut self, idx: usize, relation: Relation) {
1749 let current_relation = self.get_relation(idx).unwrap();
1750
1751 let old_root = current_relation.0;
1752 let new_root = relation.0;
1753 let mut prev = new_root.first_child_or_token();
1755 let mut new_head_len = 0;
1756 while let Some(p) = prev {
1758 if p.kind() == WHITESPACE || p.kind() == NEWLINE {
1759 new_head_len += 1;
1760 prev = p.next_sibling_or_token();
1761 } else {
1762 break;
1763 }
1764 }
1765 let mut new_tail_len = 0;
1766 let mut next = new_root.last_child_or_token();
1767 while let Some(n) = next {
1768 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1769 new_tail_len += 1;
1770 next = n.prev_sibling_or_token();
1771 } else {
1772 break;
1773 }
1774 }
1775 let mut prev = old_root.first_child_or_token();
1777 let mut old_head = vec![];
1778 while let Some(p) = prev {
1779 if p.kind() == WHITESPACE || p.kind() == NEWLINE {
1780 old_head.push(p.clone());
1781 prev = p.next_sibling_or_token();
1782 } else {
1783 break;
1784 }
1785 }
1786 let mut old_tail = vec![];
1787 let mut next = old_root.last_child_or_token();
1788 while let Some(n) = next {
1789 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1790 old_tail.push(n.clone());
1791 next = n.prev_sibling_or_token();
1792 } else {
1793 break;
1794 }
1795 }
1796 new_root.splice_children(0..new_head_len, old_head);
1797 let tail_pos = new_root.children_with_tokens().count() - new_tail_len;
1798 new_root.splice_children(
1799 tail_pos - new_tail_len..tail_pos,
1800 old_tail.into_iter().rev(),
1801 );
1802 let index = old_root.index();
1803 self.0
1804 .splice_children(index..index + 1, vec![new_root.into()]);
1805 }
1806
1807 #[must_use]
1809 pub fn wrap_and_sort(&self) -> Self {
1810 let mut relations = self
1811 .relations()
1812 .map(|r| r.wrap_and_sort())
1813 .collect::<Vec<_>>();
1814 relations.sort();
1816 Self::from(relations)
1817 }
1818
1819 pub fn relations(&self) -> impl Iterator<Item = Relation> + '_ {
1821 self.0.children().filter_map(Relation::cast)
1822 }
1823
1824 pub fn iter(&self) -> impl Iterator<Item = Relation> + '_ {
1826 self.relations()
1827 }
1828
1829 pub fn get_relation(&self, idx: usize) -> Option<Relation> {
1831 self.relations().nth(idx)
1832 }
1833
1834 pub fn remove_relation(&self, idx: usize) -> Relation {
1847 let mut relation = self.get_relation(idx).unwrap();
1848 relation.remove();
1849 relation
1850 }
1851
1852 pub fn satisfied_by(&self, package_version: impl crate::VersionLookup + Copy) -> bool {
1868 self.relations().any(|r| {
1869 let actual = package_version.lookup_version(r.name().as_str());
1870 if let Some((vc, version)) = r.version() {
1871 if let Some(actual) = actual {
1872 match vc {
1873 VersionConstraint::GreaterThanEqual => *actual >= version,
1874 VersionConstraint::LessThanEqual => *actual <= version,
1875 VersionConstraint::Equal => *actual == version,
1876 VersionConstraint::GreaterThan => *actual > version,
1877 VersionConstraint::LessThan => *actual < version,
1878 }
1879 } else {
1880 false
1881 }
1882 } else {
1883 actual.is_some()
1884 }
1885 })
1886 }
1887
1888 pub fn remove(&mut self) {
1899 let mut removed_comma = false;
1900 let is_first = !self
1901 .0
1902 .siblings(Direction::Prev)
1903 .skip(1)
1904 .any(|n| n.kind() == ENTRY);
1905 while let Some(n) = self.0.next_sibling_or_token() {
1906 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1907 n.detach();
1908 } else if n.kind() == COMMA {
1909 n.detach();
1910 removed_comma = true;
1911 break;
1912 } else {
1913 panic!("Unexpected node: {:?}", n);
1914 }
1915 }
1916 if !is_first {
1917 while let Some(n) = self.0.prev_sibling_or_token() {
1918 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1919 n.detach();
1920 } else if !removed_comma && n.kind() == COMMA {
1921 n.detach();
1922 break;
1923 } else {
1924 break;
1925 }
1926 }
1927 } else {
1928 while let Some(n) = self.0.next_sibling_or_token() {
1929 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1930 n.detach();
1931 } else {
1932 break;
1933 }
1934 }
1935 }
1936 self.0.detach();
1937 }
1938
1939 pub fn is_empty(&self) -> bool {
1941 self.relations().count() == 0
1942 }
1943
1944 pub fn len(&self) -> usize {
1946 self.relations().count()
1947 }
1948
1949 pub fn push(&mut self, relation: Relation) {
1962 let is_empty = !self
1963 .0
1964 .children_with_tokens()
1965 .any(|n| n.kind() == PIPE || n.kind() == RELATION);
1966
1967 let (position, new_children) = if let Some(current_relation) = self.relations().last() {
1968 let to_insert: Vec<NodeOrToken<GreenNode, GreenToken>> = if is_empty {
1969 vec![relation.0.green().into()]
1970 } else {
1971 vec![
1972 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
1973 NodeOrToken::Token(GreenToken::new(PIPE.into(), "|")),
1974 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
1975 relation.0.green().into(),
1976 ]
1977 };
1978
1979 (current_relation.0.index() + 1, to_insert)
1980 } else {
1981 let child_count = self.0.children_with_tokens().count();
1982 (
1983 child_count,
1984 if is_empty {
1985 vec![relation.0.green().into()]
1986 } else {
1987 vec![
1988 NodeOrToken::Token(GreenToken::new(PIPE.into(), "|")),
1989 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
1990 relation.0.green().into(),
1991 ]
1992 },
1993 )
1994 };
1995
1996 let new_root = SyntaxNode::new_root_mut(
1997 self.0.replace_with(
1998 self.0
1999 .green()
2000 .splice_children(position..position, new_children),
2001 ),
2002 );
2003
2004 if let Some(parent) = self.0.parent() {
2005 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2006 self.0 = parent
2007 .children_with_tokens()
2008 .nth(self.0.index())
2009 .unwrap()
2010 .clone()
2011 .into_node()
2012 .unwrap();
2013 } else {
2014 self.0 = new_root;
2015 }
2016 }
2017
2018 pub fn is_implied_by(&self, outer: &Entry) -> bool {
2043 if self == outer {
2045 return true;
2046 }
2047
2048 for inner_rel in self.relations() {
2050 for outer_rel in outer.relations() {
2051 if inner_rel.is_implied_by(&outer_rel) {
2052 return true;
2053 }
2054 }
2055 }
2056
2057 false
2058 }
2059}
2060
2061fn inject(builder: &mut GreenNodeBuilder, node: SyntaxNode) {
2062 builder.start_node(node.kind().into());
2063 for child in node.children_with_tokens() {
2064 match child {
2065 rowan::NodeOrToken::Node(child) => {
2066 inject(builder, child);
2067 }
2068 rowan::NodeOrToken::Token(token) => {
2069 builder.token(token.kind().into(), token.text());
2070 }
2071 }
2072 }
2073 builder.finish_node();
2074}
2075
2076impl From<Vec<Relation>> for Entry {
2077 fn from(relations: Vec<Relation>) -> Self {
2078 let mut builder = GreenNodeBuilder::new();
2079 builder.start_node(SyntaxKind::ENTRY.into());
2080 for (i, relation) in relations.into_iter().enumerate() {
2081 if i > 0 {
2082 builder.token(WHITESPACE.into(), " ");
2083 builder.token(COMMA.into(), "|");
2084 builder.token(WHITESPACE.into(), " ");
2085 }
2086 inject(&mut builder, relation.0);
2087 }
2088 builder.finish_node();
2089 Entry(SyntaxNode::new_root_mut(builder.finish()))
2090 }
2091}
2092
2093impl From<Relation> for Entry {
2094 fn from(relation: Relation) -> Self {
2095 Self::from(vec![relation])
2096 }
2097}
2098
2099fn tokenize_version(builder: &mut GreenNodeBuilder, version: &Version) {
2102 let version_str = version.to_string();
2103
2104 if let Some(colon_pos) = version_str.find(':') {
2106 builder.token(IDENT.into(), &version_str[..colon_pos]);
2108 builder.token(COLON.into(), ":");
2109 builder.token(IDENT.into(), &version_str[colon_pos + 1..]);
2111 } else {
2112 builder.token(IDENT.into(), version_str.as_str());
2114 }
2115}
2116
2117impl Relation {
2118 pub fn new(name: &str, version_constraint: Option<(VersionConstraint, Version)>) -> Self {
2132 let mut builder = GreenNodeBuilder::new();
2133 builder.start_node(SyntaxKind::RELATION.into());
2134 builder.token(IDENT.into(), name);
2135 if let Some((vc, version)) = version_constraint {
2136 builder.token(WHITESPACE.into(), " ");
2137 builder.start_node(SyntaxKind::VERSION.into());
2138 builder.token(L_PARENS.into(), "(");
2139 builder.start_node(SyntaxKind::CONSTRAINT.into());
2140 for c in vc.to_string().chars() {
2141 builder.token(
2142 match c {
2143 '>' => R_ANGLE.into(),
2144 '<' => L_ANGLE.into(),
2145 '=' => EQUAL.into(),
2146 _ => unreachable!(),
2147 },
2148 c.to_string().as_str(),
2149 );
2150 }
2151 builder.finish_node();
2152
2153 builder.token(WHITESPACE.into(), " ");
2154
2155 tokenize_version(&mut builder, &version);
2156
2157 builder.token(R_PARENS.into(), ")");
2158
2159 builder.finish_node();
2160 }
2161
2162 builder.finish_node();
2163 Relation(SyntaxNode::new_root_mut(builder.finish()))
2164 }
2165
2166 #[must_use]
2175 pub fn wrap_and_sort(&self) -> Self {
2176 let mut builder = GreenNodeBuilder::new();
2177 builder.start_node(SyntaxKind::RELATION.into());
2178 builder.token(IDENT.into(), self.name().as_str());
2179 if let Some(archqual) = self.archqual() {
2180 builder.token(COLON.into(), ":");
2181 builder.token(IDENT.into(), archqual.as_str());
2182 }
2183 if let Some((vc, version)) = self.version() {
2184 builder.token(WHITESPACE.into(), " ");
2185 builder.start_node(SyntaxKind::VERSION.into());
2186 builder.token(L_PARENS.into(), "(");
2187 builder.start_node(SyntaxKind::CONSTRAINT.into());
2188 builder.token(
2189 match vc {
2190 VersionConstraint::GreaterThanEqual => R_ANGLE.into(),
2191 VersionConstraint::LessThanEqual => L_ANGLE.into(),
2192 VersionConstraint::Equal => EQUAL.into(),
2193 VersionConstraint::GreaterThan => R_ANGLE.into(),
2194 VersionConstraint::LessThan => L_ANGLE.into(),
2195 },
2196 vc.to_string().as_str(),
2197 );
2198 builder.finish_node();
2199 builder.token(WHITESPACE.into(), " ");
2200 tokenize_version(&mut builder, &version);
2201 builder.token(R_PARENS.into(), ")");
2202 builder.finish_node();
2203 }
2204 if let Some(architectures) = self.architectures() {
2205 builder.token(WHITESPACE.into(), " ");
2206 builder.start_node(ARCHITECTURES.into());
2207 builder.token(L_BRACKET.into(), "[");
2208 for (i, arch) in architectures.enumerate() {
2209 if i > 0 {
2210 builder.token(WHITESPACE.into(), " ");
2211 }
2212 builder.token(IDENT.into(), arch.as_str());
2213 }
2214 builder.token(R_BRACKET.into(), "]");
2215 builder.finish_node();
2216 }
2217 for profiles in self.profiles() {
2218 builder.token(WHITESPACE.into(), " ");
2219 builder.start_node(PROFILES.into());
2220 builder.token(L_ANGLE.into(), "<");
2221 for (i, profile) in profiles.into_iter().enumerate() {
2222 if i > 0 {
2223 builder.token(WHITESPACE.into(), " ");
2224 }
2225 match profile {
2226 BuildProfile::Disabled(name) => {
2227 builder.token(NOT.into(), "!");
2228 builder.token(IDENT.into(), name.as_str());
2229 }
2230 BuildProfile::Enabled(name) => {
2231 builder.token(IDENT.into(), name.as_str());
2232 }
2233 }
2234 }
2235 builder.token(R_ANGLE.into(), ">");
2236 builder.finish_node();
2237 }
2238 builder.finish_node();
2239 Relation(SyntaxNode::new_root_mut(builder.finish()))
2240 }
2241
2242 pub fn simple(name: &str) -> Self {
2251 Self::new(name, None)
2252 }
2253
2254 pub fn drop_constraint(&mut self) -> bool {
2265 let version_token = self.0.children().find(|n| n.kind() == VERSION);
2266 if let Some(version_token) = version_token {
2267 while let Some(prev) = version_token.prev_sibling_or_token() {
2269 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
2270 prev.detach();
2271 } else {
2272 break;
2273 }
2274 }
2275 version_token.detach();
2276 return true;
2277 }
2278
2279 false
2280 }
2281
2282 pub fn name(&self) -> String {
2291 self.0
2292 .children_with_tokens()
2293 .find_map(|it| match it {
2294 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2295 _ => None,
2296 })
2297 .unwrap()
2298 .text()
2299 .to_string()
2300 }
2301
2302 pub fn archqual(&self) -> Option<String> {
2311 let archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
2312 let node = if let Some(archqual) = archqual {
2313 archqual.children_with_tokens().find_map(|it| match it {
2314 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2315 _ => None,
2316 })
2317 } else {
2318 None
2319 };
2320 node.map(|n| n.text().to_string())
2321 }
2322
2323 pub fn set_archqual(&mut self, archqual: &str) {
2333 let mut builder = GreenNodeBuilder::new();
2334 builder.start_node(ARCHQUAL.into());
2335 builder.token(COLON.into(), ":");
2336 builder.token(IDENT.into(), archqual);
2337 builder.finish_node();
2338
2339 let node_archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
2340 if let Some(node_archqual) = node_archqual {
2341 self.0.splice_children(
2342 node_archqual.index()..node_archqual.index() + 1,
2343 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2344 );
2345 } else {
2346 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
2347 let idx = if let Some(name_node) = name_node {
2348 name_node.index() + 1
2349 } else {
2350 0
2351 };
2352 self.0.splice_children(
2353 idx..idx,
2354 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2355 );
2356 }
2357 }
2358
2359 pub fn version(&self) -> Option<(VersionConstraint, Version)> {
2361 let vc = self.0.children().find(|n| n.kind() == VERSION);
2362 let vc = vc.as_ref()?;
2363 let constraint = vc.children().find(|n| n.kind() == CONSTRAINT);
2364
2365 let version_str: String = vc
2367 .children_with_tokens()
2368 .filter_map(|it| match it {
2369 SyntaxElement::Token(token) if token.kind() == IDENT || token.kind() == COLON => {
2370 Some(token.text().to_string())
2371 }
2372 _ => None,
2373 })
2374 .collect();
2375
2376 if let Some(constraint) = constraint {
2377 if !version_str.is_empty() {
2378 let vc: VersionConstraint = constraint.to_string().parse().unwrap();
2379 Some((vc, version_str.parse().unwrap()))
2380 } else {
2381 None
2382 }
2383 } else {
2384 None
2385 }
2386 }
2387
2388 pub fn set_version(&mut self, version_constraint: Option<(VersionConstraint, Version)>) {
2399 let current_version = self.0.children().find(|n| n.kind() == VERSION);
2400 if let Some((vc, version)) = version_constraint {
2401 let mut builder = GreenNodeBuilder::new();
2402 builder.start_node(VERSION.into());
2403 builder.token(L_PARENS.into(), "(");
2404 builder.start_node(CONSTRAINT.into());
2405 match vc {
2406 VersionConstraint::GreaterThanEqual => {
2407 builder.token(R_ANGLE.into(), ">");
2408 builder.token(EQUAL.into(), "=");
2409 }
2410 VersionConstraint::LessThanEqual => {
2411 builder.token(L_ANGLE.into(), "<");
2412 builder.token(EQUAL.into(), "=");
2413 }
2414 VersionConstraint::Equal => {
2415 builder.token(EQUAL.into(), "=");
2416 }
2417 VersionConstraint::GreaterThan => {
2418 builder.token(R_ANGLE.into(), ">");
2419 }
2420 VersionConstraint::LessThan => {
2421 builder.token(L_ANGLE.into(), "<");
2422 }
2423 }
2424 builder.finish_node(); builder.token(WHITESPACE.into(), " ");
2426 tokenize_version(&mut builder, &version);
2427 builder.token(R_PARENS.into(), ")");
2428 builder.finish_node(); if let Some(current_version) = current_version {
2431 self.0.splice_children(
2432 current_version.index()..current_version.index() + 1,
2433 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2434 );
2435 } else {
2436 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
2437 let idx = if let Some(name_node) = name_node {
2438 name_node.index() + 1
2439 } else {
2440 0
2441 };
2442 let new_children = vec![
2443 GreenToken::new(WHITESPACE.into(), " ").into(),
2444 builder.finish().into(),
2445 ];
2446 let new_root = SyntaxNode::new_root_mut(
2447 self.0.green().splice_children(idx..idx, new_children),
2448 );
2449 if let Some(parent) = self.0.parent() {
2450 parent
2451 .splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2452 self.0 = parent
2453 .children_with_tokens()
2454 .nth(self.0.index())
2455 .unwrap()
2456 .clone()
2457 .into_node()
2458 .unwrap();
2459 } else {
2460 self.0 = new_root;
2461 }
2462 }
2463 } else if let Some(current_version) = current_version {
2464 while let Some(prev) = current_version.prev_sibling_or_token() {
2466 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
2467 prev.detach();
2468 } else {
2469 break;
2470 }
2471 }
2472 current_version.detach();
2473 }
2474 }
2475
2476 pub fn architectures(&self) -> Option<impl Iterator<Item = String> + '_> {
2485 let architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES)?;
2486
2487 Some(architectures.children_with_tokens().filter_map(|node| {
2488 let token = node.as_token()?;
2489 if token.kind() == IDENT {
2490 Some(token.text().to_string())
2491 } else {
2492 None
2493 }
2494 }))
2495 }
2496
2497 pub fn profiles(&self) -> impl Iterator<Item = Vec<BuildProfile>> + '_ {
2507 let profiles = self.0.children().filter(|n| n.kind() == PROFILES);
2508
2509 profiles.map(|profile| {
2510 let mut ret = vec![];
2512 let mut current = vec![];
2513 for token in profile.children_with_tokens() {
2514 match token.kind() {
2515 WHITESPACE | NEWLINE => {
2516 if !current.is_empty() {
2517 ret.push(current.join("").parse::<BuildProfile>().unwrap());
2518 current = vec![];
2519 }
2520 }
2521 L_ANGLE | R_ANGLE => {}
2522 _ => {
2523 current.push(token.to_string());
2524 }
2525 }
2526 }
2527 if !current.is_empty() {
2528 ret.push(current.concat().parse().unwrap());
2529 }
2530 ret
2531 })
2532 }
2533
2534 pub fn remove(&mut self) {
2545 let is_first = !self
2546 .0
2547 .siblings(Direction::Prev)
2548 .skip(1)
2549 .any(|n| n.kind() == RELATION);
2550 if !is_first {
2551 while let Some(n) = self.0.prev_sibling_or_token() {
2554 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2555 n.detach();
2556 } else if n.kind() == PIPE {
2557 n.detach();
2558 break;
2559 } else {
2560 break;
2561 }
2562 }
2563 while let Some(n) = self.0.prev_sibling_or_token() {
2564 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2565 n.detach();
2566 } else {
2567 break;
2568 }
2569 }
2570 } else {
2571 while let Some(n) = self.0.next_sibling_or_token() {
2574 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2575 n.detach();
2576 } else if n.kind() == PIPE {
2577 n.detach();
2578 break;
2579 } else {
2580 panic!("Unexpected node: {:?}", n);
2581 }
2582 }
2583
2584 while let Some(n) = self.0.next_sibling_or_token() {
2585 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2586 n.detach();
2587 } else {
2588 break;
2589 }
2590 }
2591 }
2592 if let Some(mut parent) = self.0.parent().and_then(Entry::cast) {
2594 if parent.is_empty() {
2595 parent.remove();
2596 } else {
2597 self.0.detach();
2598 }
2599 } else {
2600 self.0.detach();
2601 }
2602 }
2603
2604 pub fn set_architectures<'a>(&mut self, architectures: impl Iterator<Item = &'a str>) {
2614 let mut builder = GreenNodeBuilder::new();
2615 builder.start_node(ARCHITECTURES.into());
2616 builder.token(L_BRACKET.into(), "[");
2617 for (i, arch) in architectures.enumerate() {
2618 if i > 0 {
2619 builder.token(WHITESPACE.into(), " ");
2620 }
2621 builder.token(IDENT.into(), arch);
2622 }
2623 builder.token(R_BRACKET.into(), "]");
2624 builder.finish_node();
2625
2626 let node_architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES);
2627 if let Some(node_architectures) = node_architectures {
2628 let new_root = SyntaxNode::new_root_mut(builder.finish());
2629 self.0.splice_children(
2630 node_architectures.index()..node_architectures.index() + 1,
2631 vec![new_root.into()],
2632 );
2633 } else {
2634 let profiles = self.0.children().find(|n| n.kind() == PROFILES);
2635 let idx = if let Some(profiles) = profiles {
2636 profiles.index()
2637 } else {
2638 self.0.children_with_tokens().count()
2639 };
2640 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
2641 idx..idx,
2642 vec![
2643 GreenToken::new(WHITESPACE.into(), " ").into(),
2644 builder.finish().into(),
2645 ],
2646 ));
2647 if let Some(parent) = self.0.parent() {
2648 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2649 self.0 = parent
2650 .children_with_tokens()
2651 .nth(self.0.index())
2652 .unwrap()
2653 .clone()
2654 .into_node()
2655 .unwrap();
2656 } else {
2657 self.0 = new_root;
2658 }
2659 }
2660 }
2661
2662 pub fn add_profile(&mut self, profile: &[BuildProfile]) {
2673 let mut builder = GreenNodeBuilder::new();
2674 builder.start_node(PROFILES.into());
2675 builder.token(L_ANGLE.into(), "<");
2676 for (i, profile) in profile.iter().enumerate() {
2677 if i > 0 {
2678 builder.token(WHITESPACE.into(), " ");
2679 }
2680 match profile {
2681 BuildProfile::Disabled(name) => {
2682 builder.token(NOT.into(), "!");
2683 builder.token(IDENT.into(), name.as_str());
2684 }
2685 BuildProfile::Enabled(name) => {
2686 builder.token(IDENT.into(), name.as_str());
2687 }
2688 }
2689 }
2690 builder.token(R_ANGLE.into(), ">");
2691 builder.finish_node();
2692
2693 let node_profiles = self.0.children().find(|n| n.kind() == PROFILES);
2694 if let Some(node_profiles) = node_profiles {
2695 let new_root = SyntaxNode::new_root_mut(builder.finish());
2696 self.0.splice_children(
2697 node_profiles.index()..node_profiles.index() + 1,
2698 vec![new_root.into()],
2699 );
2700 } else {
2701 let idx = self.0.children_with_tokens().count();
2702 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
2703 idx..idx,
2704 vec![
2705 GreenToken::new(WHITESPACE.into(), " ").into(),
2706 builder.finish().into(),
2707 ],
2708 ));
2709 if let Some(parent) = self.0.parent() {
2710 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2711 self.0 = parent
2712 .children_with_tokens()
2713 .nth(self.0.index())
2714 .unwrap()
2715 .clone()
2716 .into_node()
2717 .unwrap();
2718 } else {
2719 self.0 = new_root;
2720 }
2721 }
2722 }
2723
2724 pub fn build(name: &str) -> RelationBuilder {
2726 RelationBuilder::new(name)
2727 }
2728
2729 pub fn is_implied_by(&self, outer: &Relation) -> bool {
2756 if self.name() != outer.name() {
2757 return false;
2758 }
2759
2760 let inner_version = self.version();
2761 let outer_version = outer.version();
2762
2763 if inner_version.is_none() {
2765 return true;
2766 }
2767
2768 if inner_version == outer_version {
2770 return true;
2771 }
2772
2773 if outer_version.is_none() {
2775 return false;
2776 }
2777
2778 let (inner_constraint, inner_ver) = inner_version.unwrap();
2779 let (outer_constraint, outer_ver) = outer_version.unwrap();
2780
2781 use VersionConstraint::*;
2782 match inner_constraint {
2783 GreaterThanEqual => match outer_constraint {
2784 GreaterThan => outer_ver > inner_ver,
2785 GreaterThanEqual | Equal => outer_ver >= inner_ver,
2786 LessThan | LessThanEqual => false,
2787 },
2788 Equal => match outer_constraint {
2789 Equal => outer_ver == inner_ver,
2790 _ => false,
2791 },
2792 LessThan => match outer_constraint {
2793 LessThan => outer_ver <= inner_ver,
2794 LessThanEqual | Equal => outer_ver < inner_ver,
2795 GreaterThan | GreaterThanEqual => false,
2796 },
2797 LessThanEqual => match outer_constraint {
2798 LessThanEqual | Equal | LessThan => outer_ver <= inner_ver,
2799 GreaterThan | GreaterThanEqual => false,
2800 },
2801 GreaterThan => match outer_constraint {
2802 GreaterThan => outer_ver >= inner_ver,
2803 Equal | GreaterThanEqual => outer_ver > inner_ver,
2804 LessThan | LessThanEqual => false,
2805 },
2806 }
2807 }
2808}
2809
2810pub struct RelationBuilder {
2824 name: String,
2825 version_constraint: Option<(VersionConstraint, Version)>,
2826 archqual: Option<String>,
2827 architectures: Option<Vec<String>>,
2828 profiles: Vec<Vec<BuildProfile>>,
2829}
2830
2831impl RelationBuilder {
2832 fn new(name: &str) -> Self {
2834 Self {
2835 name: name.to_string(),
2836 version_constraint: None,
2837 archqual: None,
2838 architectures: None,
2839 profiles: vec![],
2840 }
2841 }
2842
2843 pub fn version_constraint(mut self, vc: VersionConstraint, version: Version) -> Self {
2845 self.version_constraint = Some((vc, version));
2846 self
2847 }
2848
2849 pub fn archqual(mut self, archqual: &str) -> Self {
2851 self.archqual = Some(archqual.to_string());
2852 self
2853 }
2854
2855 pub fn architectures(mut self, architectures: Vec<String>) -> Self {
2857 self.architectures = Some(architectures);
2858 self
2859 }
2860
2861 pub fn profiles(mut self, profiles: Vec<Vec<BuildProfile>>) -> Self {
2863 self.profiles = profiles;
2864 self
2865 }
2866
2867 pub fn add_profile(mut self, profile: Vec<BuildProfile>) -> Self {
2869 self.profiles.push(profile);
2870 self
2871 }
2872
2873 pub fn build(self) -> Relation {
2875 let mut relation = Relation::new(&self.name, self.version_constraint);
2876 if let Some(archqual) = &self.archqual {
2877 relation.set_archqual(archqual);
2878 }
2879 if let Some(architectures) = &self.architectures {
2880 relation.set_architectures(architectures.iter().map(|s| s.as_str()));
2881 }
2882 for profile in &self.profiles {
2883 relation.add_profile(profile);
2884 }
2885 relation
2886 }
2887}
2888
2889impl PartialOrd for Relation {
2890 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
2891 let name_cmp = self.name().cmp(&other.name());
2893 if name_cmp != std::cmp::Ordering::Equal {
2894 return Some(name_cmp);
2895 }
2896
2897 let self_version = self.version();
2898 let other_version = other.version();
2899
2900 match (self_version, other_version) {
2901 (Some((self_vc, self_version)), Some((other_vc, other_version))) => {
2902 let vc_cmp = self_vc.cmp(&other_vc);
2903 if vc_cmp != std::cmp::Ordering::Equal {
2904 return Some(vc_cmp);
2905 }
2906
2907 Some(self_version.cmp(&other_version))
2908 }
2909 (Some(_), None) => Some(std::cmp::Ordering::Greater),
2910 (None, Some(_)) => Some(std::cmp::Ordering::Less),
2911 (None, None) => Some(std::cmp::Ordering::Equal),
2912 }
2913 }
2914}
2915
2916impl Eq for Relation {}
2917
2918impl Ord for Relation {
2919 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
2920 self.partial_cmp(other).unwrap()
2921 }
2922}
2923
2924impl std::str::FromStr for Relations {
2925 type Err = String;
2926
2927 fn from_str(s: &str) -> Result<Self, Self::Err> {
2928 let parse = parse(s, false);
2929 if parse.errors.is_empty() {
2930 Ok(parse.root_mut())
2931 } else {
2932 Err(parse.errors.join("\n"))
2933 }
2934 }
2935}
2936
2937impl std::str::FromStr for Entry {
2938 type Err = String;
2939
2940 fn from_str(s: &str) -> Result<Self, Self::Err> {
2941 let root: Relations = s.parse()?;
2942
2943 let mut entries = root.entries();
2944 let entry = if let Some(entry) = entries.next() {
2945 entry
2946 } else {
2947 return Err("No entry found".to_string());
2948 };
2949
2950 if entries.next().is_some() {
2951 return Err("Multiple entries found".to_string());
2952 }
2953
2954 Ok(entry)
2955 }
2956}
2957
2958impl std::str::FromStr for Relation {
2959 type Err = String;
2960
2961 fn from_str(s: &str) -> Result<Self, Self::Err> {
2962 let entry: Entry = s.parse()?;
2963
2964 let mut relations = entry.relations();
2965 let relation = if let Some(relation) = relations.next() {
2966 relation
2967 } else {
2968 return Err("No relation found".to_string());
2969 };
2970
2971 if relations.next().is_some() {
2972 return Err("Multiple relations found".to_string());
2973 }
2974
2975 Ok(relation)
2976 }
2977}
2978
2979impl From<crate::lossy::Relation> for Relation {
2980 fn from(relation: crate::lossy::Relation) -> Self {
2981 let mut builder = Relation::build(&relation.name);
2982
2983 if let Some((vc, version)) = relation.version {
2984 builder = builder.version_constraint(vc, version);
2985 }
2986
2987 if let Some(archqual) = relation.archqual {
2988 builder = builder.archqual(&archqual);
2989 }
2990
2991 if let Some(architectures) = relation.architectures {
2992 builder = builder.architectures(architectures);
2993 }
2994
2995 builder = builder.profiles(relation.profiles);
2996
2997 builder.build()
2998 }
2999}
3000
3001impl From<Relation> for crate::lossy::Relation {
3002 fn from(relation: Relation) -> Self {
3003 crate::lossy::Relation {
3004 name: relation.name(),
3005 version: relation.version(),
3006 archqual: relation.archqual(),
3007 architectures: relation.architectures().map(|a| a.collect()),
3008 profiles: relation.profiles().collect(),
3009 }
3010 }
3011}
3012
3013impl From<Entry> for Vec<crate::lossy::Relation> {
3014 fn from(entry: Entry) -> Self {
3015 entry.relations().map(|r| r.into()).collect()
3016 }
3017}
3018
3019impl From<Vec<crate::lossy::Relation>> for Entry {
3020 fn from(relations: Vec<crate::lossy::Relation>) -> Self {
3021 let relations: Vec<Relation> = relations.into_iter().map(|r| r.into()).collect();
3022 Entry::from(relations)
3023 }
3024}
3025
3026#[cfg(test)]
3027mod tests {
3028 use super::*;
3029
3030 #[test]
3031 fn test_parse() {
3032 let input = "python3-dulwich";
3033 let parsed: Relations = input.parse().unwrap();
3034 assert_eq!(parsed.to_string(), input);
3035 assert_eq!(parsed.entries().count(), 1);
3036 let entry = parsed.entries().next().unwrap();
3037 assert_eq!(entry.to_string(), "python3-dulwich");
3038 assert_eq!(entry.relations().count(), 1);
3039 let relation = entry.relations().next().unwrap();
3040 assert_eq!(relation.to_string(), "python3-dulwich");
3041 assert_eq!(relation.version(), None);
3042
3043 let input = "python3-dulwich (>= 0.20.21)";
3044 let parsed: Relations = input.parse().unwrap();
3045 assert_eq!(parsed.to_string(), input);
3046 assert_eq!(parsed.entries().count(), 1);
3047 let entry = parsed.entries().next().unwrap();
3048 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3049 assert_eq!(entry.relations().count(), 1);
3050 let relation = entry.relations().next().unwrap();
3051 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
3052 assert_eq!(
3053 relation.version(),
3054 Some((
3055 VersionConstraint::GreaterThanEqual,
3056 "0.20.21".parse().unwrap()
3057 ))
3058 );
3059 }
3060
3061 #[test]
3062 fn test_multiple() {
3063 let input = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)";
3064 let parsed: Relations = input.parse().unwrap();
3065 assert_eq!(parsed.to_string(), input);
3066 assert_eq!(parsed.entries().count(), 2);
3067 let entry = parsed.entries().next().unwrap();
3068 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3069 assert_eq!(entry.relations().count(), 1);
3070 let relation = entry.relations().next().unwrap();
3071 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
3072 assert_eq!(
3073 relation.version(),
3074 Some((
3075 VersionConstraint::GreaterThanEqual,
3076 "0.20.21".parse().unwrap()
3077 ))
3078 );
3079 let entry = parsed.entries().nth(1).unwrap();
3080 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.21)");
3081 assert_eq!(entry.relations().count(), 1);
3082 let relation = entry.relations().next().unwrap();
3083 assert_eq!(relation.to_string(), "python3-dulwich (<< 0.21)");
3084 assert_eq!(
3085 relation.version(),
3086 Some((VersionConstraint::LessThan, "0.21".parse().unwrap()))
3087 );
3088 }
3089
3090 #[test]
3091 fn test_architectures() {
3092 let input = "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]";
3093 let parsed: Relations = input.parse().unwrap();
3094 assert_eq!(parsed.to_string(), input);
3095 assert_eq!(parsed.entries().count(), 1);
3096 let entry = parsed.entries().next().unwrap();
3097 assert_eq!(
3098 entry.to_string(),
3099 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
3100 );
3101 assert_eq!(entry.relations().count(), 1);
3102 let relation = entry.relations().next().unwrap();
3103 assert_eq!(
3104 relation.to_string(),
3105 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
3106 );
3107 assert_eq!(relation.version(), None);
3108 assert_eq!(
3109 relation.architectures().unwrap().collect::<Vec<_>>(),
3110 vec![
3111 "amd64", "arm64", "armhf", "i386", "mips", "mips64el", "mipsel", "ppc64el", "s390x"
3112 ]
3113 .into_iter()
3114 .map(|s| s.to_string())
3115 .collect::<Vec<_>>()
3116 );
3117 }
3118
3119 #[test]
3120 fn test_profiles() {
3121 let input = "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>, bar";
3122 let parsed: Relations = input.parse().unwrap();
3123 assert_eq!(parsed.to_string(), input);
3124 assert_eq!(parsed.entries().count(), 2);
3125 let entry = parsed.entries().next().unwrap();
3126 assert_eq!(
3127 entry.to_string(),
3128 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
3129 );
3130 assert_eq!(entry.relations().count(), 1);
3131 let relation = entry.relations().next().unwrap();
3132 assert_eq!(
3133 relation.to_string(),
3134 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
3135 );
3136 assert_eq!(
3137 relation.version(),
3138 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap()))
3139 );
3140 assert_eq!(
3141 relation.architectures().unwrap().collect::<Vec<_>>(),
3142 vec!["i386", "arm"]
3143 .into_iter()
3144 .map(|s| s.to_string())
3145 .collect::<Vec<_>>()
3146 );
3147 assert_eq!(
3148 relation.profiles().collect::<Vec<_>>(),
3149 vec![
3150 vec![BuildProfile::Disabled("nocheck".to_string())],
3151 vec![BuildProfile::Disabled("cross".to_string())]
3152 ]
3153 );
3154 }
3155
3156 #[test]
3157 fn test_substvar() {
3158 let input = "${shlibs:Depends}";
3159
3160 let (parsed, errors) = Relations::parse_relaxed(input, true);
3161 assert_eq!(errors, Vec::<String>::new());
3162 assert_eq!(parsed.to_string(), input);
3163 assert_eq!(parsed.entries().count(), 0);
3164
3165 assert_eq!(
3166 parsed.substvars().collect::<Vec<_>>(),
3167 vec!["${shlibs:Depends}"]
3168 );
3169 }
3170
3171 #[test]
3172 fn test_new() {
3173 let r = Relation::new(
3174 "samba",
3175 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
3176 );
3177
3178 assert_eq!(r.to_string(), "samba (>= 2.0)");
3179 }
3180
3181 #[test]
3182 fn test_drop_constraint() {
3183 let mut r = Relation::new(
3184 "samba",
3185 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
3186 );
3187
3188 r.drop_constraint();
3189
3190 assert_eq!(r.to_string(), "samba");
3191 }
3192
3193 #[test]
3194 fn test_simple() {
3195 let r = Relation::simple("samba");
3196
3197 assert_eq!(r.to_string(), "samba");
3198 }
3199
3200 #[test]
3201 fn test_remove_first_entry() {
3202 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3203 .parse()
3204 .unwrap();
3205 let removed = rels.remove_entry(0);
3206 assert_eq!(removed.to_string(), "python3-dulwich (>= 0.20.21)");
3207 assert_eq!(rels.to_string(), "python3-dulwich (<< 0.21)");
3208 }
3209
3210 #[test]
3211 fn test_remove_last_entry() {
3212 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3213 .parse()
3214 .unwrap();
3215 rels.remove_entry(1);
3216 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
3217 }
3218
3219 #[test]
3220 fn test_remove_middle() {
3221 let mut rels: Relations =
3222 r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21), python3-dulwich (<< 0.22)"#
3223 .parse()
3224 .unwrap();
3225 rels.remove_entry(1);
3226 assert_eq!(
3227 rels.to_string(),
3228 "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.22)"
3229 );
3230 }
3231
3232 #[test]
3233 fn test_remove_added() {
3234 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
3235 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3236 rels.push(entry);
3237 rels.remove_entry(1);
3238 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
3239 }
3240
3241 #[test]
3242 fn test_push() {
3243 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
3244 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3245 rels.push(entry);
3246 assert_eq!(
3247 rels.to_string(),
3248 "python3-dulwich (>= 0.20.21), python3-dulwich"
3249 );
3250 }
3251
3252 #[test]
3253 fn test_insert_with_custom_separator() {
3254 let mut rels: Relations = "python3".parse().unwrap();
3255 let entry = Entry::from(vec![Relation::simple("debhelper")]);
3256 rels.insert_with_separator(1, entry, Some("\n "));
3257 assert_eq!(rels.to_string(), "python3,\n debhelper");
3258 }
3259
3260 #[test]
3261 fn test_insert_with_wrap_and_sort_separator() {
3262 let mut rels: Relations = "python3".parse().unwrap();
3263 let entry = Entry::from(vec![Relation::simple("rustc")]);
3264 rels.insert_with_separator(1, entry, Some("\n "));
3266 assert_eq!(rels.to_string(), "python3,\n rustc");
3267 }
3268
3269 #[test]
3270 fn test_push_from_empty() {
3271 let mut rels: Relations = "".parse().unwrap();
3272 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3273 rels.push(entry);
3274 assert_eq!(rels.to_string(), "python3-dulwich");
3275 }
3276
3277 #[test]
3278 fn test_insert() {
3279 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3280 .parse()
3281 .unwrap();
3282 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3283 rels.insert(1, entry);
3284 assert_eq!(
3285 rels.to_string(),
3286 "python3-dulwich (>= 0.20.21), python3-dulwich, python3-dulwich (<< 0.21)"
3287 );
3288 }
3289
3290 #[test]
3291 fn test_insert_at_start() {
3292 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3293 .parse()
3294 .unwrap();
3295 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3296 rels.insert(0, entry);
3297 assert_eq!(
3298 rels.to_string(),
3299 "python3-dulwich, python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3300 );
3301 }
3302
3303 #[test]
3304 fn test_insert_after_error() {
3305 let (mut rels, errors) = Relations::parse_relaxed("@foo@, debhelper (>= 1.0)", false);
3306 assert_eq!(
3307 errors,
3308 vec![
3309 "expected $ or identifier but got ERROR",
3310 "expected comma or end of file but got Some(IDENT)",
3311 "expected $ or identifier but got ERROR"
3312 ]
3313 );
3314 let entry = Entry::from(vec![Relation::simple("bar")]);
3315 rels.push(entry);
3316 assert_eq!(rels.to_string(), "@foo@, debhelper (>= 1.0), bar");
3317 }
3318
3319 #[test]
3320 fn test_insert_before_error() {
3321 let (mut rels, errors) = Relations::parse_relaxed("debhelper (>= 1.0), @foo@, bla", false);
3322 assert_eq!(
3323 errors,
3324 vec![
3325 "expected $ or identifier but got ERROR",
3326 "expected comma or end of file but got Some(IDENT)",
3327 "expected $ or identifier but got ERROR"
3328 ]
3329 );
3330 let entry = Entry::from(vec![Relation::simple("bar")]);
3331 rels.insert(0, entry);
3332 assert_eq!(rels.to_string(), "bar, debhelper (>= 1.0), @foo@, bla");
3333 }
3334
3335 #[test]
3336 fn test_replace() {
3337 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3338 .parse()
3339 .unwrap();
3340 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3341 rels.replace(1, entry);
3342 assert_eq!(
3343 rels.to_string(),
3344 "python3-dulwich (>= 0.20.21), python3-dulwich"
3345 );
3346 }
3347
3348 #[test]
3349 fn test_relation_from_entries() {
3350 let entries = vec![
3351 Entry::from(vec![Relation::simple("python3-dulwich")]),
3352 Entry::from(vec![Relation::simple("python3-breezy")]),
3353 ];
3354 let rels: Relations = entries.into();
3355 assert_eq!(rels.entries().count(), 2);
3356 assert_eq!(rels.to_string(), "python3-dulwich, python3-breezy");
3357 }
3358
3359 #[test]
3360 fn test_entry_from_relations() {
3361 let relations = vec![
3362 Relation::simple("python3-dulwich"),
3363 Relation::simple("python3-breezy"),
3364 ];
3365 let entry: Entry = relations.into();
3366 assert_eq!(entry.relations().count(), 2);
3367 assert_eq!(entry.to_string(), "python3-dulwich | python3-breezy");
3368 }
3369
3370 #[test]
3371 fn test_parse_entry() {
3372 let parsed: Entry = "python3-dulwich (>= 0.20.21) | bar".parse().unwrap();
3373 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21) | bar");
3374 assert_eq!(parsed.relations().count(), 2);
3375
3376 assert_eq!(
3377 "foo, bar".parse::<Entry>().unwrap_err(),
3378 "Multiple entries found"
3379 );
3380 assert_eq!("".parse::<Entry>().unwrap_err(), "No entry found");
3381 }
3382
3383 #[test]
3384 fn test_parse_relation() {
3385 let parsed: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3386 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21)");
3387 assert_eq!(
3388 parsed.version(),
3389 Some((
3390 VersionConstraint::GreaterThanEqual,
3391 "0.20.21".parse().unwrap()
3392 ))
3393 );
3394 assert_eq!(
3395 "foo | bar".parse::<Relation>().unwrap_err(),
3396 "Multiple relations found"
3397 );
3398 assert_eq!("".parse::<Relation>().unwrap_err(), "No entry found");
3399 }
3400
3401 #[test]
3402 fn test_special() {
3403 let parsed: Relation = "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
3404 .parse()
3405 .unwrap();
3406 assert_eq!(
3407 parsed.to_string(),
3408 "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
3409 );
3410 assert_eq!(
3411 parsed.version(),
3412 Some((
3413 VersionConstraint::GreaterThanEqual,
3414 "0.1.138-~~".parse().unwrap()
3415 ))
3416 );
3417 assert_eq!(parsed.archqual(), Some("amd64".to_string()));
3418 assert_eq!(parsed.name(), "librust-breezyshim+dirty-tracker-dev");
3419 }
3420
3421 #[test]
3422 fn test_relations_satisfied_by() {
3423 let rels: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3424 .parse()
3425 .unwrap();
3426 let satisfied = |name: &str| -> Option<debversion::Version> {
3427 match name {
3428 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
3429 _ => None,
3430 }
3431 };
3432 assert!(rels.satisfied_by(satisfied));
3433
3434 let satisfied = |name: &str| match name {
3435 "python3-dulwich" => Some("0.21".parse().unwrap()),
3436 _ => None,
3437 };
3438 assert!(!rels.satisfied_by(satisfied));
3439
3440 let satisfied = |name: &str| match name {
3441 "python3-dulwich" => Some("0.20.20".parse().unwrap()),
3442 _ => None,
3443 };
3444 assert!(!rels.satisfied_by(satisfied));
3445 }
3446
3447 #[test]
3448 fn test_entry_satisfied_by() {
3449 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3450 .parse()
3451 .unwrap();
3452 let satisfied = |name: &str| -> Option<debversion::Version> {
3453 match name {
3454 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
3455 _ => None,
3456 }
3457 };
3458 assert!(entry.satisfied_by(satisfied));
3459 let satisfied = |name: &str| -> Option<debversion::Version> {
3460 match name {
3461 "python3-dulwich" => Some("0.18".parse().unwrap()),
3462 _ => None,
3463 }
3464 };
3465 assert!(!entry.satisfied_by(satisfied));
3466 }
3467
3468 #[test]
3469 fn test_wrap_and_sort_relation() {
3470 let relation: Relation = " python3-dulwich (>= 11) [ amd64 ] < lala>"
3471 .parse()
3472 .unwrap();
3473
3474 let wrapped = relation.wrap_and_sort();
3475
3476 assert_eq!(
3477 wrapped.to_string(),
3478 "python3-dulwich (>= 11) [amd64] <lala>"
3479 );
3480 }
3481
3482 #[test]
3483 fn test_wrap_and_sort_relations() {
3484 let entry: Relations =
3485 "python3-dulwich (>= 0.20.21) | bar, \n\n\n\npython3-dulwich (<< 0.21)"
3486 .parse()
3487 .unwrap();
3488
3489 let wrapped = entry.wrap_and_sort();
3490
3491 assert_eq!(
3492 wrapped.to_string(),
3493 "bar | python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3494 );
3495 }
3496
3497 #[cfg(feature = "serde")]
3498 #[test]
3499 fn test_serialize_relations() {
3500 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3501 .parse()
3502 .unwrap();
3503 let serialized = serde_json::to_string(&relations).unwrap();
3504 assert_eq!(
3505 serialized,
3506 r#""python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)""#
3507 );
3508 }
3509
3510 #[cfg(feature = "serde")]
3511 #[test]
3512 fn test_deserialize_relations() {
3513 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3514 .parse()
3515 .unwrap();
3516 let serialized = serde_json::to_string(&relations).unwrap();
3517 let deserialized: Relations = serde_json::from_str(&serialized).unwrap();
3518 assert_eq!(deserialized.to_string(), relations.to_string());
3519 }
3520
3521 #[cfg(feature = "serde")]
3522 #[test]
3523 fn test_serialize_relation() {
3524 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3525 let serialized = serde_json::to_string(&relation).unwrap();
3526 assert_eq!(serialized, r#""python3-dulwich (>= 0.20.21)""#);
3527 }
3528
3529 #[cfg(feature = "serde")]
3530 #[test]
3531 fn test_deserialize_relation() {
3532 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3533 let serialized = serde_json::to_string(&relation).unwrap();
3534 let deserialized: Relation = serde_json::from_str(&serialized).unwrap();
3535 assert_eq!(deserialized.to_string(), relation.to_string());
3536 }
3537
3538 #[cfg(feature = "serde")]
3539 #[test]
3540 fn test_serialize_entry() {
3541 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3542 .parse()
3543 .unwrap();
3544 let serialized = serde_json::to_string(&entry).unwrap();
3545 assert_eq!(
3546 serialized,
3547 r#""python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)""#
3548 );
3549 }
3550
3551 #[cfg(feature = "serde")]
3552 #[test]
3553 fn test_deserialize_entry() {
3554 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3555 .parse()
3556 .unwrap();
3557 let serialized = serde_json::to_string(&entry).unwrap();
3558 let deserialized: Entry = serde_json::from_str(&serialized).unwrap();
3559 assert_eq!(deserialized.to_string(), entry.to_string());
3560 }
3561
3562 #[test]
3563 fn test_remove_first_relation() {
3564 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3565 .parse()
3566 .unwrap();
3567 let mut rel = entry.relations().next().unwrap();
3568 rel.remove();
3569 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.18)");
3570 }
3571
3572 #[test]
3573 fn test_remove_last_relation() {
3574 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3575 .parse()
3576 .unwrap();
3577 let mut rel = entry.relations().nth(1).unwrap();
3578 rel.remove();
3579 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3580 }
3581
3582 #[test]
3583 fn test_remove_only_relation() {
3584 let entry: Entry = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3585 let mut rel = entry.relations().next().unwrap();
3586 rel.remove();
3587 assert_eq!(entry.to_string(), "");
3588 }
3589
3590 #[test]
3591 fn test_relations_is_empty() {
3592 let entry: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3593 assert!(!entry.is_empty());
3594 assert_eq!(1, entry.len());
3595 let mut rel = entry.entries().next().unwrap();
3596 rel.remove();
3597 assert!(entry.is_empty());
3598 assert_eq!(0, entry.len());
3599 }
3600
3601 #[test]
3602 fn test_entry_is_empty() {
3603 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3604 .parse()
3605 .unwrap();
3606 assert!(!entry.is_empty());
3607 assert_eq!(2, entry.len());
3608 let mut rel = entry.relations().next().unwrap();
3609 rel.remove();
3610 assert!(!entry.is_empty());
3611 assert_eq!(1, entry.len());
3612 let mut rel = entry.relations().next().unwrap();
3613 rel.remove();
3614 assert!(entry.is_empty());
3615 assert_eq!(0, entry.len());
3616 }
3617
3618 #[test]
3619 fn test_relation_set_version() {
3620 let mut rel: Relation = "samba".parse().unwrap();
3621 rel.set_version(None);
3622 assert_eq!("samba", rel.to_string());
3623 rel.set_version(Some((
3624 VersionConstraint::GreaterThanEqual,
3625 "2.0".parse().unwrap(),
3626 )));
3627 assert_eq!("samba (>= 2.0)", rel.to_string());
3628 rel.set_version(None);
3629 assert_eq!("samba", rel.to_string());
3630 rel.set_version(Some((
3631 VersionConstraint::GreaterThanEqual,
3632 "2.0".parse().unwrap(),
3633 )));
3634 rel.set_version(Some((
3635 VersionConstraint::GreaterThanEqual,
3636 "1.1".parse().unwrap(),
3637 )));
3638 assert_eq!("samba (>= 1.1)", rel.to_string());
3639 }
3640
3641 #[test]
3642 fn test_replace_relation() {
3643 let mut entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3644 .parse()
3645 .unwrap();
3646 let new_rel = Relation::simple("python3-breezy");
3647 entry.replace(0, new_rel);
3648 assert_eq!(
3649 entry.to_string(),
3650 "python3-breezy | python3-dulwich (<< 0.18)"
3651 );
3652 }
3653
3654 #[test]
3655 fn test_entry_push_relation() {
3656 let relations: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3657 let new_rel = Relation::simple("python3-breezy");
3658 let mut entry = relations.entries().next().unwrap();
3659 entry.push(new_rel);
3660 assert_eq!(
3661 entry.to_string(),
3662 "python3-dulwich (>= 0.20.21) | python3-breezy"
3663 );
3664 assert_eq!(
3665 relations.to_string(),
3666 "python3-dulwich (>= 0.20.21) | python3-breezy"
3667 );
3668 }
3669
3670 #[test]
3671 fn test_relations_remove_empty_entry() {
3672 let (mut relations, errors) = Relations::parse_relaxed("foo, , bar, ", false);
3673 assert_eq!(errors, Vec::<String>::new());
3674 assert_eq!(relations.to_string(), "foo, , bar, ");
3675 assert_eq!(relations.len(), 2);
3676 assert_eq!(
3677 relations.entries().next().unwrap().to_string(),
3678 "foo".to_string()
3679 );
3680 assert_eq!(
3681 relations.entries().nth(1).unwrap().to_string(),
3682 "bar".to_string()
3683 );
3684 relations.remove_entry(1);
3685 assert_eq!(relations.to_string(), "foo, , ");
3686 }
3687
3688 #[test]
3689 fn test_entry_remove_relation() {
3690 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
3691 let removed = entry.remove_relation(0);
3692 assert_eq!(removed.to_string(), "python3-dulwich");
3693 assert_eq!(entry.to_string(), "samba");
3694 }
3695
3696 #[test]
3697 fn test_wrap_and_sort_removes_empty_entries() {
3698 let relations: Relations = "foo, , bar, ".parse().unwrap();
3699 let wrapped = relations.wrap_and_sort();
3700 assert_eq!(wrapped.to_string(), "bar, foo");
3701 }
3702
3703 #[test]
3704 fn test_set_archqual() {
3705 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
3706 let mut rel = entry.relations().next().unwrap();
3707 rel.set_archqual("amd64");
3708 assert_eq!(rel.to_string(), "python3-dulwich:amd64");
3709 assert_eq!(rel.archqual(), Some("amd64".to_string()));
3710 assert_eq!(entry.to_string(), "python3-dulwich:amd64 | samba");
3711 rel.set_archqual("i386");
3712 assert_eq!(rel.to_string(), "python3-dulwich:i386");
3713 assert_eq!(rel.archqual(), Some("i386".to_string()));
3714 assert_eq!(entry.to_string(), "python3-dulwich:i386 | samba");
3715 }
3716
3717 #[test]
3718 fn test_set_architectures() {
3719 let mut relation = Relation::simple("samba");
3720 relation.set_architectures(vec!["amd64", "i386"].into_iter());
3721 assert_eq!(relation.to_string(), "samba [amd64 i386]");
3722 }
3723
3724 #[test]
3725 fn test_relation_builder_no_architectures() {
3726 let relation = Relation::build("debhelper").build();
3728 assert_eq!(relation.to_string(), "debhelper");
3729 }
3730
3731 #[test]
3732 fn test_relation_builder_with_architectures() {
3733 let relation = Relation::build("samba")
3735 .architectures(vec!["amd64".to_string(), "i386".to_string()])
3736 .build();
3737 assert_eq!(relation.to_string(), "samba [amd64 i386]");
3738 }
3739
3740 #[test]
3741 fn test_ensure_minimum_version_add_new() {
3742 let mut relations: Relations = "python3".parse().unwrap();
3743 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3744 assert_eq!(relations.to_string(), "python3, debhelper (>= 12)");
3745 }
3746
3747 #[test]
3748 fn test_ensure_minimum_version_update() {
3749 let mut relations: Relations = "debhelper (>= 9)".parse().unwrap();
3750 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3751 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3752 }
3753
3754 #[test]
3755 fn test_ensure_minimum_version_no_change() {
3756 let mut relations: Relations = "debhelper (>= 13)".parse().unwrap();
3757 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3758 assert_eq!(relations.to_string(), "debhelper (>= 13)");
3759 }
3760
3761 #[test]
3762 fn test_ensure_minimum_version_no_version() {
3763 let mut relations: Relations = "debhelper".parse().unwrap();
3764 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3765 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3766 }
3767
3768 #[test]
3769 fn test_ensure_minimum_version_preserves_newline() {
3770 let input = "\n debhelper (>= 9),\n pkg-config,\n uuid-dev";
3776 let mut relations: Relations = input.parse().unwrap();
3777 relations.ensure_minimum_version("debhelper", &"12~".parse().unwrap());
3778 let result = relations.to_string();
3779
3780 assert!(
3782 result.starts_with('\n'),
3783 "Expected result to start with newline, got: {:?}",
3784 result
3785 );
3786 assert_eq!(result, "\n debhelper (>= 12~),\n pkg-config,\n uuid-dev");
3787 }
3788
3789 #[test]
3790 fn test_ensure_minimum_version_preserves_newline_in_control() {
3791 use crate::lossless::Control;
3793 use std::str::FromStr;
3794
3795 let input = r#"Source: f2fs-tools
3796Section: admin
3797Priority: optional
3798Maintainer: Test <test@example.com>
3799Build-Depends:
3800 debhelper (>= 9),
3801 pkg-config,
3802 uuid-dev
3803
3804Package: f2fs-tools
3805Description: test
3806"#;
3807
3808 let control = Control::from_str(input).unwrap();
3809 let mut source = control.source().unwrap();
3810 let mut build_depends = source.build_depends().unwrap();
3811
3812 let version = Version::from_str("12~").unwrap();
3813 build_depends.ensure_minimum_version("debhelper", &version);
3814
3815 source.set_build_depends(&build_depends);
3816
3817 let output = control.to_string();
3818
3819 assert!(
3821 output.contains("Build-Depends:\n debhelper (>= 12~)"),
3822 "Expected 'Build-Depends:\\n debhelper (>= 12~)' but got:\n{}",
3823 output
3824 );
3825 }
3826
3827 #[test]
3828 fn test_ensure_exact_version_add_new() {
3829 let mut relations: Relations = "python3".parse().unwrap();
3830 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3831 assert_eq!(relations.to_string(), "python3, debhelper (= 12)");
3832 }
3833
3834 #[test]
3835 fn test_ensure_exact_version_update() {
3836 let mut relations: Relations = "debhelper (>= 9)".parse().unwrap();
3837 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3838 assert_eq!(relations.to_string(), "debhelper (= 12)");
3839 }
3840
3841 #[test]
3842 fn test_ensure_exact_version_no_change() {
3843 let mut relations: Relations = "debhelper (= 12)".parse().unwrap();
3844 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3845 assert_eq!(relations.to_string(), "debhelper (= 12)");
3846 }
3847
3848 #[test]
3849 fn test_ensure_some_version_add_new() {
3850 let mut relations: Relations = "python3".parse().unwrap();
3851 relations.ensure_some_version("debhelper");
3852 assert_eq!(relations.to_string(), "python3, debhelper");
3853 }
3854
3855 #[test]
3856 fn test_ensure_some_version_exists_with_version() {
3857 let mut relations: Relations = "debhelper (>= 12)".parse().unwrap();
3858 relations.ensure_some_version("debhelper");
3859 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3860 }
3861
3862 #[test]
3863 fn test_ensure_some_version_exists_no_version() {
3864 let mut relations: Relations = "debhelper".parse().unwrap();
3865 relations.ensure_some_version("debhelper");
3866 assert_eq!(relations.to_string(), "debhelper");
3867 }
3868
3869 #[test]
3870 fn test_ensure_substvar() {
3871 let mut relations: Relations = "python3".parse().unwrap();
3872 relations.ensure_substvar("${misc:Depends}").unwrap();
3873 assert_eq!(relations.to_string(), "python3, ${misc:Depends}");
3874 }
3875
3876 #[test]
3877 fn test_ensure_substvar_already_exists() {
3878 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
3879 relations.ensure_substvar("${misc:Depends}").unwrap();
3880 assert_eq!(relations.to_string(), "python3, ${misc:Depends}");
3881 }
3882
3883 #[test]
3884 fn test_ensure_substvar_empty_relations() {
3885 let mut relations: Relations = Relations::new();
3886 relations.ensure_substvar("${misc:Depends}").unwrap();
3887 assert_eq!(relations.to_string(), "${misc:Depends}");
3888 }
3889
3890 #[test]
3891 fn test_ensure_substvar_preserves_whitespace() {
3892 let (mut relations, _) = Relations::parse_relaxed("python3, rustc", false);
3894 relations.ensure_substvar("${misc:Depends}").unwrap();
3895 assert_eq!(relations.to_string(), "python3, rustc, ${misc:Depends}");
3897 }
3898
3899 #[test]
3900 fn test_drop_substvar_basic() {
3901 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
3902 relations.drop_substvar("${misc:Depends}");
3903 assert_eq!(relations.to_string(), "python3");
3904 }
3905
3906 #[test]
3907 fn test_drop_substvar_first_position() {
3908 let (mut relations, _) = Relations::parse_relaxed("${misc:Depends}, python3", true);
3909 relations.drop_substvar("${misc:Depends}");
3910 assert_eq!(relations.to_string(), "python3");
3911 }
3912
3913 #[test]
3914 fn test_drop_substvar_middle_position() {
3915 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}, rustc", true);
3916 relations.drop_substvar("${misc:Depends}");
3917 assert_eq!(relations.to_string(), "python3, rustc");
3918 }
3919
3920 #[test]
3921 fn test_drop_substvar_only_substvar() {
3922 let (mut relations, _) = Relations::parse_relaxed("${misc:Depends}", true);
3923 relations.drop_substvar("${misc:Depends}");
3924 assert_eq!(relations.to_string(), "");
3925 }
3926
3927 #[test]
3928 fn test_drop_substvar_not_exists() {
3929 let (mut relations, _) = Relations::parse_relaxed("python3, rustc", false);
3930 relations.drop_substvar("${misc:Depends}");
3931 assert_eq!(relations.to_string(), "python3, rustc");
3932 }
3933
3934 #[test]
3935 fn test_drop_substvar_multiple_substvars() {
3936 let (mut relations, _) =
3937 Relations::parse_relaxed("python3, ${misc:Depends}, ${shlibs:Depends}", true);
3938 relations.drop_substvar("${misc:Depends}");
3939 assert_eq!(relations.to_string(), "python3, ${shlibs:Depends}");
3940 }
3941
3942 #[test]
3943 fn test_drop_substvar_preserves_whitespace() {
3944 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
3945 relations.drop_substvar("${misc:Depends}");
3946 assert_eq!(relations.to_string(), "python3");
3947 }
3948
3949 #[test]
3950 fn test_filter_entries_basic() {
3951 let mut relations: Relations = "python3, debhelper, rustc".parse().unwrap();
3952 relations.filter_entries(|entry| entry.relations().any(|r| r.name().starts_with("python")));
3953 assert_eq!(relations.to_string(), "python3");
3954 }
3955
3956 #[test]
3957 fn test_filter_entries_keep_all() {
3958 let mut relations: Relations = "python3, debhelper".parse().unwrap();
3959 relations.filter_entries(|_| true);
3960 assert_eq!(relations.to_string(), "python3, debhelper");
3961 }
3962
3963 #[test]
3964 fn test_filter_entries_remove_all() {
3965 let mut relations: Relations = "python3, debhelper".parse().unwrap();
3966 relations.filter_entries(|_| false);
3967 assert_eq!(relations.to_string(), "");
3968 }
3969
3970 #[test]
3971 fn test_filter_entries_keep_middle() {
3972 let mut relations: Relations = "aaa, bbb, ccc".parse().unwrap();
3973 relations.filter_entries(|entry| entry.relations().any(|r| r.name() == "bbb"));
3974 assert_eq!(relations.to_string(), "bbb");
3975 }
3976
3977 #[test]
3980 fn test_is_sorted_wrap_and_sort_order() {
3981 let relations: Relations = "debhelper, python3, rustc".parse().unwrap();
3983 assert!(relations.is_sorted(&WrapAndSortOrder));
3984
3985 let relations: Relations = "rustc, debhelper, python3".parse().unwrap();
3987 assert!(!relations.is_sorted(&WrapAndSortOrder));
3988
3989 let (relations, _) =
3991 Relations::parse_relaxed("cdbs, debhelper-compat, python3, ${misc:Depends}", true);
3992 assert!(relations.is_sorted(&WrapAndSortOrder));
3993 }
3994
3995 #[test]
3996 fn test_is_sorted_default_order() {
3997 let relations: Relations = "aaa, bbb, ccc".parse().unwrap();
3999 assert!(relations.is_sorted(&DefaultSortingOrder));
4000
4001 let relations: Relations = "ccc, aaa, bbb".parse().unwrap();
4003 assert!(!relations.is_sorted(&DefaultSortingOrder));
4004
4005 let (relations, _) = Relations::parse_relaxed("aaa, bbb, ${misc:Depends}", true);
4007 assert!(relations.is_sorted(&DefaultSortingOrder));
4008 }
4009
4010 #[test]
4011 fn test_is_sorted_with_substvars() {
4012 let (relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}, rustc", true);
4014 assert!(relations.is_sorted(&DefaultSortingOrder));
4016 }
4017
4018 #[test]
4019 fn test_drop_dependency_exists() {
4020 let mut relations: Relations = "python3, debhelper, rustc".parse().unwrap();
4021 assert!(relations.drop_dependency("debhelper"));
4022 assert_eq!(relations.to_string(), "python3, rustc");
4023 }
4024
4025 #[test]
4026 fn test_drop_dependency_not_exists() {
4027 let mut relations: Relations = "python3, rustc".parse().unwrap();
4028 assert!(!relations.drop_dependency("nonexistent"));
4029 assert_eq!(relations.to_string(), "python3, rustc");
4030 }
4031
4032 #[test]
4033 fn test_drop_dependency_only_item() {
4034 let mut relations: Relations = "python3".parse().unwrap();
4035 assert!(relations.drop_dependency("python3"));
4036 assert_eq!(relations.to_string(), "");
4037 }
4038
4039 #[test]
4040 fn test_add_dependency_to_empty() {
4041 let mut relations: Relations = "".parse().unwrap();
4042 let entry = Entry::from(Relation::simple("python3"));
4043 relations.add_dependency(entry, None);
4044 assert_eq!(relations.to_string(), "python3");
4045 }
4046
4047 #[test]
4048 fn test_add_dependency_sorted_position() {
4049 let mut relations: Relations = "debhelper, rustc".parse().unwrap();
4050 let entry = Entry::from(Relation::simple("python3"));
4051 relations.add_dependency(entry, None);
4052 assert_eq!(relations.to_string(), "debhelper, python3, rustc");
4054 }
4055
4056 #[test]
4057 fn test_add_dependency_explicit_position() {
4058 let mut relations: Relations = "python3, rustc".parse().unwrap();
4059 let entry = Entry::from(Relation::simple("debhelper"));
4060 relations.add_dependency(entry, Some(0));
4061 assert_eq!(relations.to_string(), "debhelper, python3, rustc");
4062 }
4063
4064 #[test]
4065 fn test_add_dependency_build_system_first() {
4066 let mut relations: Relations = "python3, rustc".parse().unwrap();
4067 let entry = Entry::from(Relation::simple("debhelper-compat"));
4068 relations.add_dependency(entry, None);
4069 assert_eq!(relations.to_string(), "debhelper-compat, python3, rustc");
4071 }
4072
4073 #[test]
4074 fn test_add_dependency_at_end() {
4075 let mut relations: Relations = "debhelper, python3".parse().unwrap();
4076 let entry = Entry::from(Relation::simple("zzz-package"));
4077 relations.add_dependency(entry, None);
4078 assert_eq!(relations.to_string(), "debhelper, python3, zzz-package");
4080 }
4081
4082 #[test]
4083 fn test_add_dependency_to_single_entry() {
4084 let mut relations: Relations = "python3-dulwich".parse().unwrap();
4086 let entry: Entry = "debhelper-compat (= 12)".parse().unwrap();
4087 relations.add_dependency(entry, None);
4088 assert_eq!(
4090 relations.to_string(),
4091 "debhelper-compat (= 12), python3-dulwich"
4092 );
4093 }
4094
4095 #[test]
4096 fn test_get_relation_exists() {
4097 let relations: Relations = "python3, debhelper (>= 12), rustc".parse().unwrap();
4098 let result = relations.get_relation("debhelper");
4099 assert!(result.is_ok());
4100 let (idx, entry) = result.unwrap();
4101 assert_eq!(idx, 1);
4102 assert_eq!(entry.to_string(), "debhelper (>= 12)");
4103 }
4104
4105 #[test]
4106 fn test_get_relation_not_exists() {
4107 let relations: Relations = "python3, rustc".parse().unwrap();
4108 let result = relations.get_relation("nonexistent");
4109 assert_eq!(result, Err("Package nonexistent not found".to_string()));
4110 }
4111
4112 #[test]
4113 fn test_get_relation_complex_rule() {
4114 let relations: Relations = "python3 | python3-minimal, rustc".parse().unwrap();
4115 let result = relations.get_relation("python3");
4116 assert_eq!(
4117 result,
4118 Err("Complex rule for python3, aborting".to_string())
4119 );
4120 }
4121
4122 #[test]
4123 fn test_iter_relations_for_simple() {
4124 let relations: Relations = "python3, debhelper, python3-dev".parse().unwrap();
4125 let entries: Vec<_> = relations.iter_relations_for("python3").collect();
4126 assert_eq!(entries.len(), 1);
4127 assert_eq!(entries[0].0, 0);
4128 assert_eq!(entries[0].1.to_string(), "python3");
4129 }
4130
4131 #[test]
4132 fn test_iter_relations_for_alternatives() {
4133 let relations: Relations = "python3 | python3-minimal, python3-dev".parse().unwrap();
4134 let entries: Vec<_> = relations.iter_relations_for("python3").collect();
4135 assert_eq!(entries.len(), 1);
4137 assert_eq!(entries[0].0, 0);
4138 }
4139
4140 #[test]
4141 fn test_iter_relations_for_not_found() {
4142 let relations: Relations = "python3, rustc".parse().unwrap();
4143 let entries: Vec<_> = relations.iter_relations_for("debhelper").collect();
4144 assert_eq!(entries.len(), 0);
4145 }
4146
4147 #[test]
4148 fn test_has_relation_exists() {
4149 let relations: Relations = "python3, debhelper, rustc".parse().unwrap();
4150 assert!(relations.has_relation("debhelper"));
4151 assert!(relations.has_relation("python3"));
4152 assert!(relations.has_relation("rustc"));
4153 }
4154
4155 #[test]
4156 fn test_has_relation_not_exists() {
4157 let relations: Relations = "python3, rustc".parse().unwrap();
4158 assert!(!relations.has_relation("debhelper"));
4159 }
4160
4161 #[test]
4162 fn test_has_relation_in_alternative() {
4163 let relations: Relations = "python3 | python3-minimal".parse().unwrap();
4164 assert!(relations.has_relation("python3"));
4165 assert!(relations.has_relation("python3-minimal"));
4166 }
4167
4168 #[test]
4169 fn test_sorting_order_wrap_and_sort_build_systems() {
4170 let order = WrapAndSortOrder;
4171 assert!(order.lt("debhelper", "python3"));
4173 assert!(order.lt("debhelper-compat", "rustc"));
4174 assert!(order.lt("cdbs", "aaa"));
4175 assert!(order.lt("dh-python", "python3"));
4176 }
4177
4178 #[test]
4179 fn test_sorting_order_wrap_and_sort_regular_packages() {
4180 let order = WrapAndSortOrder;
4181 assert!(order.lt("aaa", "bbb"));
4183 assert!(order.lt("python3", "rustc"));
4184 assert!(!order.lt("rustc", "python3"));
4185 }
4186
4187 #[test]
4188 fn test_sorting_order_wrap_and_sort_substvars() {
4189 let order = WrapAndSortOrder;
4190 assert!(order.lt("python3", "${misc:Depends}"));
4192 assert!(!order.lt("${misc:Depends}", "python3"));
4193 assert!(!order.ignore("${misc:Depends}"));
4195 }
4196
4197 #[test]
4198 fn test_sorting_order_default_special_items() {
4199 let order = DefaultSortingOrder;
4200 assert!(order.lt("python3", "${misc:Depends}"));
4202 assert!(order.lt("aaa", "@cdbs@"));
4203 assert!(order.ignore("${misc:Depends}"));
4205 assert!(order.ignore("@cdbs@"));
4206 assert!(!order.ignore("python3"));
4207 }
4208
4209 #[test]
4210 fn test_is_special_package_name() {
4211 assert!(is_special_package_name("${misc:Depends}"));
4212 assert!(is_special_package_name("${shlibs:Depends}"));
4213 assert!(is_special_package_name("@cdbs@"));
4214 assert!(!is_special_package_name("python3"));
4215 assert!(!is_special_package_name("debhelper"));
4216 }
4217
4218 #[test]
4219 fn test_add_dependency_with_explicit_position() {
4220 let mut relations: Relations = "python3, rustc".parse().unwrap();
4222 let entry = Entry::from(Relation::simple("debhelper"));
4223 relations.add_dependency(entry, Some(1));
4224 assert_eq!(relations.to_string(), "python3, debhelper, rustc");
4226 }
4227
4228 #[test]
4229 fn test_whitespace_detection_single_space() {
4230 let mut relations: Relations = "python3, rustc".parse().unwrap();
4231 let entry = Entry::from(Relation::simple("debhelper"));
4232 relations.add_dependency(entry, Some(1));
4233 assert_eq!(relations.to_string(), "python3, debhelper, rustc");
4234 }
4235
4236 #[test]
4237 fn test_whitespace_detection_multiple_spaces() {
4238 let mut relations: Relations = "python3, rustc, gcc".parse().unwrap();
4239 let entry = Entry::from(Relation::simple("debhelper"));
4240 relations.add_dependency(entry, Some(1));
4241 assert_eq!(relations.to_string(), "python3, debhelper, rustc, gcc");
4243 }
4244
4245 #[test]
4246 fn test_whitespace_detection_mixed_patterns() {
4247 let mut relations: Relations = "a, b, c, d, e".parse().unwrap();
4249 let entry = Entry::from(Relation::simple("x"));
4250 relations.push(entry);
4251 assert_eq!(relations.to_string(), "a, b, c, d, e, x");
4254 }
4255
4256 #[test]
4257 fn test_whitespace_detection_newlines() {
4258 let mut relations: Relations = "python3,\n rustc".parse().unwrap();
4259 let entry = Entry::from(Relation::simple("debhelper"));
4260 relations.add_dependency(entry, Some(1));
4261 assert_eq!(relations.to_string(), "python3,\n debhelper,\n rustc");
4263 }
4264
4265 #[test]
4266 fn test_append_with_newline_no_trailing() {
4267 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4268 let entry = Entry::from(Relation::simple("blah"));
4269 relations.add_dependency(entry, None);
4270 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4271 }
4272
4273 #[test]
4274 fn test_append_with_trailing_newline() {
4275 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4276 let entry = Entry::from(Relation::simple("blah"));
4277 relations.add_dependency(entry, None);
4278 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4279 }
4280
4281 #[test]
4282 fn test_append_with_4_space_indent() {
4283 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4284 let entry = Entry::from(Relation::simple("blah"));
4285 relations.add_dependency(entry, None);
4286 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4287 }
4288
4289 #[test]
4290 fn test_append_with_4_space_and_trailing_newline() {
4291 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4292 let entry = Entry::from(Relation::simple("blah"));
4293 relations.add_dependency(entry, None);
4294 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4295 }
4296
4297 #[test]
4298 fn test_odd_syntax_append_no_trailing() {
4299 let mut relations: Relations = "\n foo\n , bar".parse().unwrap();
4300 let entry = Entry::from(Relation::simple("blah"));
4301 relations.add_dependency(entry, None);
4302 assert_eq!(relations.to_string(), "\n foo\n , bar\n , blah");
4303 }
4304
4305 #[test]
4306 fn test_odd_syntax_append_with_trailing() {
4307 let mut relations: Relations = "\n foo\n , bar\n".parse().unwrap();
4308 let entry = Entry::from(Relation::simple("blah"));
4309 relations.add_dependency(entry, None);
4310 assert_eq!(relations.to_string(), "\n foo\n , bar\n , blah");
4311 }
4312
4313 #[test]
4314 fn test_insert_at_1_no_trailing() {
4315 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4316 let entry = Entry::from(Relation::simple("blah"));
4317 relations.add_dependency(entry, Some(1));
4318 assert_eq!(relations.to_string(), "foo,\n blah,\n bar");
4319 }
4320
4321 #[test]
4322 fn test_insert_at_1_with_trailing() {
4323 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4324 let entry = Entry::from(Relation::simple("blah"));
4325 relations.add_dependency(entry, Some(1));
4326 assert_eq!(relations.to_string(), "foo,\n blah,\n bar");
4327 }
4328
4329 #[test]
4330 fn test_odd_syntax_insert_at_1() {
4331 let mut relations: Relations = "\n foo\n , bar\n".parse().unwrap();
4332 let entry = Entry::from(Relation::simple("blah"));
4333 relations.add_dependency(entry, Some(1));
4334 assert_eq!(relations.to_string(), "\n foo\n , blah\n , bar");
4335 }
4336
4337 #[test]
4338 fn test_relations_preserves_exact_whitespace() {
4339 let input =
4341 "debhelper (>= 10), quilt (>= 0.40),\n libsystemd-dev [linux-any], pkg-config";
4342
4343 let relations: Relations = input.parse().unwrap();
4344
4345 assert_eq!(
4347 relations.to_string(),
4348 input,
4349 "Relations should preserve exact whitespace from input"
4350 );
4351 }
4352
4353 #[test]
4354 fn test_remove_entry_preserves_indentation() {
4355 let input = "debhelper (>= 10), quilt (>= 0.40),\n libsystemd-dev [linux-any], dh-systemd (>= 1.5), pkg-config";
4357
4358 let mut relations: Relations = input.parse().unwrap();
4359
4360 let mut to_remove = Vec::new();
4362 for (idx, entry) in relations.entries().enumerate() {
4363 for relation in entry.relations() {
4364 if relation.name() == "dh-systemd" {
4365 to_remove.push(idx);
4366 break;
4367 }
4368 }
4369 }
4370
4371 for idx in to_remove.into_iter().rev() {
4372 relations.remove_entry(idx);
4373 }
4374
4375 let output = relations.to_string();
4376 println!("After removal: '{}'", output);
4377
4378 assert!(
4380 output.contains("\n libsystemd-dev"),
4381 "Expected 4-space indentation to be preserved, but got:\n'{}'",
4382 output
4383 );
4384 }
4385
4386 #[test]
4387 fn test_relation_is_implied_by_same_package() {
4388 let inner = Relation::new(
4390 "pkg",
4391 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4392 );
4393 let outer = Relation::new(
4394 "pkg",
4395 Some((VersionConstraint::GreaterThanEqual, "1.5".parse().unwrap())),
4396 );
4397 assert!(inner.is_implied_by(&outer));
4398 }
4399
4400 #[test]
4401 fn test_relation_is_implied_by_different_package() {
4402 let inner = Relation::new("pkg1", None);
4404 let outer = Relation::new("pkg2", None);
4405 assert!(!inner.is_implied_by(&outer));
4406 }
4407
4408 #[test]
4409 fn test_relation_is_implied_by_no_version() {
4410 let inner = Relation::new("pkg", None);
4412 let outer = Relation::new(
4413 "pkg",
4414 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4415 );
4416 assert!(inner.is_implied_by(&outer));
4417 }
4418
4419 #[test]
4420 fn test_relation_is_implied_by_identical() {
4421 let inner = Relation::new(
4423 "pkg",
4424 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4425 );
4426 let outer = Relation::new(
4427 "pkg",
4428 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4429 );
4430 assert!(inner.is_implied_by(&outer));
4431 assert!(outer.is_implied_by(&inner));
4432 }
4433
4434 #[test]
4435 fn test_relation_is_implied_by_greater_than_equal() {
4436 let inner = Relation::new(
4438 "pkg",
4439 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4440 );
4441 let outer = Relation::new(
4442 "pkg",
4443 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
4444 );
4445 assert!(inner.is_implied_by(&outer));
4446 assert!(!outer.is_implied_by(&inner));
4447
4448 let outer = Relation::new(
4450 "pkg",
4451 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4452 );
4453 assert!(inner.is_implied_by(&outer));
4454
4455 let outer = Relation::new(
4457 "pkg",
4458 Some((VersionConstraint::GreaterThan, "1.5".parse().unwrap())),
4459 );
4460 assert!(inner.is_implied_by(&outer));
4461
4462 let inner = Relation::new(
4464 "pkg",
4465 Some((VersionConstraint::GreaterThanEqual, "3.0".parse().unwrap())),
4466 );
4467 let outer = Relation::new(
4468 "pkg",
4469 Some((VersionConstraint::GreaterThan, "3.0".parse().unwrap())),
4470 );
4471 assert!(!inner.is_implied_by(&outer));
4472 }
4473
4474 #[test]
4475 fn test_relation_is_implied_by_less_than_equal() {
4476 let inner = Relation::new(
4478 "pkg",
4479 Some((VersionConstraint::LessThanEqual, "2.0".parse().unwrap())),
4480 );
4481 let outer = Relation::new(
4482 "pkg",
4483 Some((VersionConstraint::LessThanEqual, "1.0".parse().unwrap())),
4484 );
4485 assert!(inner.is_implied_by(&outer));
4486 assert!(!outer.is_implied_by(&inner));
4487
4488 let outer = Relation::new(
4490 "pkg",
4491 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4492 );
4493 assert!(inner.is_implied_by(&outer));
4494
4495 let outer = Relation::new(
4497 "pkg",
4498 Some((VersionConstraint::LessThan, "1.5".parse().unwrap())),
4499 );
4500 assert!(inner.is_implied_by(&outer));
4501 }
4502
4503 #[test]
4504 fn test_relation_is_implied_by_equal() {
4505 let inner = Relation::new(
4507 "pkg",
4508 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4509 );
4510 let outer = Relation::new(
4511 "pkg",
4512 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4513 );
4514 assert!(inner.is_implied_by(&outer));
4515
4516 let outer = Relation::new(
4518 "pkg",
4519 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4520 );
4521 assert!(!inner.is_implied_by(&outer));
4522
4523 let outer = Relation::new(
4525 "pkg",
4526 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4527 );
4528 assert!(!inner.is_implied_by(&outer));
4529 }
4530
4531 #[test]
4532 fn test_relation_is_implied_by_greater_than() {
4533 let inner = Relation::new(
4535 "pkg",
4536 Some((VersionConstraint::GreaterThan, "1.0".parse().unwrap())),
4537 );
4538 let outer = Relation::new(
4539 "pkg",
4540 Some((VersionConstraint::GreaterThan, "2.0".parse().unwrap())),
4541 );
4542 assert!(inner.is_implied_by(&outer));
4543
4544 let outer = Relation::new(
4546 "pkg",
4547 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4548 );
4549 assert!(inner.is_implied_by(&outer));
4550
4551 let outer = Relation::new(
4553 "pkg",
4554 Some((VersionConstraint::GreaterThanEqual, "1.5".parse().unwrap())),
4555 );
4556 assert!(inner.is_implied_by(&outer));
4557
4558 let outer = Relation::new(
4560 "pkg",
4561 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4562 );
4563 assert!(!inner.is_implied_by(&outer));
4564 }
4565
4566 #[test]
4567 fn test_relation_is_implied_by_less_than() {
4568 let inner = Relation::new(
4570 "pkg",
4571 Some((VersionConstraint::LessThan, "2.0".parse().unwrap())),
4572 );
4573 let outer = Relation::new(
4574 "pkg",
4575 Some((VersionConstraint::LessThan, "1.0".parse().unwrap())),
4576 );
4577 assert!(inner.is_implied_by(&outer));
4578
4579 let outer = Relation::new(
4581 "pkg",
4582 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4583 );
4584 assert!(inner.is_implied_by(&outer));
4585
4586 let outer = Relation::new(
4588 "pkg",
4589 Some((VersionConstraint::LessThanEqual, "1.5".parse().unwrap())),
4590 );
4591 assert!(inner.is_implied_by(&outer));
4592 }
4593
4594 #[test]
4595 fn test_relation_is_implied_by_incompatible_constraints() {
4596 let inner = Relation::new(
4598 "pkg",
4599 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4600 );
4601 let outer = Relation::new(
4602 "pkg",
4603 Some((VersionConstraint::LessThanEqual, "2.0".parse().unwrap())),
4604 );
4605 assert!(!inner.is_implied_by(&outer));
4606 assert!(!outer.is_implied_by(&inner));
4607 }
4608
4609 #[test]
4610 fn test_entry_is_implied_by_identical() {
4611 let inner: Entry = "pkg (>= 1.0)".parse().unwrap();
4612 let outer: Entry = "pkg (>= 1.0)".parse().unwrap();
4613 assert!(inner.is_implied_by(&outer));
4614 }
4615
4616 #[test]
4617 fn test_entry_is_implied_by_or_group() {
4618 let inner: Entry = "pkg (>= 1.0)".parse().unwrap();
4620 let outer: Entry = "pkg (>= 1.5) | libc6".parse().unwrap();
4621 assert!(inner.is_implied_by(&outer));
4622 }
4623
4624 #[test]
4625 fn test_entry_is_implied_by_simple_or() {
4626 let inner: Entry = "pkg1 | pkg2".parse().unwrap();
4628 let outer: Entry = "pkg1".parse().unwrap();
4629 assert!(inner.is_implied_by(&outer));
4630
4631 let outer: Entry = "pkg2".parse().unwrap();
4633 assert!(inner.is_implied_by(&outer));
4634 }
4635
4636 #[test]
4637 fn test_entry_is_implied_by_not_implied() {
4638 let inner: Entry = "pkg (>= 2.0)".parse().unwrap();
4640 let outer: Entry = "pkg (>= 1.0)".parse().unwrap();
4641 assert!(!inner.is_implied_by(&outer));
4642 }
4643
4644 #[test]
4645 fn test_entry_is_implied_by_different_packages() {
4646 let inner: Entry = "pkg1".parse().unwrap();
4647 let outer: Entry = "pkg2".parse().unwrap();
4648 assert!(!inner.is_implied_by(&outer));
4649 }
4650
4651 #[test]
4652 fn test_entry_is_implied_by_complex_or() {
4653 let inner: Entry = "pkg1 | pkg2".parse().unwrap();
4655 let outer: Entry = "pkg1 | pkg2".parse().unwrap();
4656 assert!(inner.is_implied_by(&outer));
4657
4658 let outer: Entry = "pkg1 | pkg2 | pkg3".parse().unwrap();
4660 assert!(inner.is_implied_by(&outer));
4661 }
4662
4663 #[test]
4664 fn test_parse_version_with_epoch() {
4665 let input = "amule-dbg (<< 1:2.3.2-2~)";
4668 let parsed: Relations = input.parse().unwrap();
4669 assert_eq!(parsed.to_string(), input);
4670 assert_eq!(parsed.entries().count(), 1);
4671 let entry = parsed.entries().next().unwrap();
4672 assert_eq!(entry.to_string(), "amule-dbg (<< 1:2.3.2-2~)");
4673 assert_eq!(entry.relations().count(), 1);
4674 let relation = entry.relations().next().unwrap();
4675 assert_eq!(relation.name(), "amule-dbg");
4676 assert_eq!(relation.to_string(), "amule-dbg (<< 1:2.3.2-2~)");
4677 assert_eq!(
4678 relation.version(),
4679 Some((VersionConstraint::LessThan, "1:2.3.2-2~".parse().unwrap()))
4680 );
4681 }
4682}