1use crate::relations::SyntaxKind::{self, *};
22use crate::relations::{BuildProfile, VersionConstraint};
23use debversion::Version;
24use rowan::{Direction, NodeOrToken};
25use std::collections::HashSet;
26
27#[derive(Debug, Clone, PartialEq, Eq, Hash)]
29pub struct ParseError(Vec<String>);
30
31impl std::fmt::Display for ParseError {
32 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
33 for err in &self.0 {
34 writeln!(f, "{}", err)?;
35 }
36 Ok(())
37 }
38}
39
40impl std::error::Error for ParseError {}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
46enum Lang {}
47impl rowan::Language for Lang {
48 type Kind = SyntaxKind;
49 fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
50 unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
51 }
52 fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
53 kind.into()
54 }
55}
56
57use rowan::{GreenNode, GreenToken};
60
61use rowan::GreenNodeBuilder;
65
66struct Parse {
69 green_node: GreenNode,
70 #[allow(unused)]
71 errors: Vec<String>,
72}
73
74fn parse(text: &str, allow_substvar: bool) -> Parse {
75 struct Parser {
76 tokens: Vec<(SyntaxKind, String)>,
79 builder: GreenNodeBuilder<'static>,
81 errors: Vec<String>,
84 allow_substvar: bool,
86 }
87
88 impl Parser {
89 fn parse_substvar(&mut self) {
90 self.builder.start_node(SyntaxKind::SUBSTVAR.into());
91 self.bump();
92 if self.current() != Some(L_CURLY) {
93 self.error(format!("expected {{ but got {:?}", self.current()).to_string());
94 } else {
95 self.bump();
96 }
97 loop {
98 match self.current() {
99 Some(IDENT) | Some(COLON) => {
100 self.bump();
101 }
102 Some(R_CURLY) => {
103 break;
104 }
105 e => {
106 self.error(format!("expected identifier or : but got {:?}", e).to_string());
107 }
108 }
109 }
110 if self.current() != Some(R_CURLY) {
111 self.error(format!("expected }} but got {:?}", self.current()).to_string());
112 } else {
113 self.bump();
114 }
115 self.builder.finish_node();
116 }
117
118 fn parse_entry(&mut self) {
119 self.skip_ws();
120 self.builder.start_node(SyntaxKind::ENTRY.into());
121 loop {
122 self.parse_relation();
123 match self.peek_past_ws() {
124 Some(COMMA) => {
125 break;
126 }
127 Some(PIPE) => {
128 self.skip_ws();
129 self.bump();
130 self.skip_ws();
131 }
132 None => {
133 self.skip_ws();
134 break;
135 }
136 _ => {
137 self.skip_ws();
138 self.builder.start_node(SyntaxKind::ERROR.into());
139 match self.tokens.pop() {
140 Some((k, t)) => {
141 self.builder.token(k.into(), t.as_str());
142 self.errors
143 .push(format!("Expected comma or pipe, not {:?}", (k, t)));
144 }
145 None => {
146 self.errors
147 .push("Expected comma or pipe, got end of file".to_string());
148 }
149 }
150 self.builder.finish_node();
151 }
152 }
153 }
154 self.builder.finish_node();
155 }
156
157 fn error(&mut self, error: String) {
158 self.errors.push(error);
159 self.builder.start_node(SyntaxKind::ERROR.into());
160 if self.current().is_some() {
161 self.bump();
162 }
163 self.builder.finish_node();
164 }
165
166 fn parse_relation(&mut self) {
167 self.builder.start_node(SyntaxKind::RELATION.into());
168 if self.current() == Some(IDENT) {
169 self.bump();
170 } else {
171 self.error("Expected package name".to_string());
172 }
173 match self.peek_past_ws() {
174 Some(COLON) => {
175 self.skip_ws();
176 self.builder.start_node(ARCHQUAL.into());
177 self.bump();
178 self.skip_ws();
179 if self.current() == Some(IDENT) {
180 self.bump();
181 } else {
182 self.error("Expected architecture name".to_string());
183 }
184 self.builder.finish_node();
185 self.skip_ws();
186 }
187 Some(PIPE) | Some(COMMA) => {}
188 None | Some(L_PARENS) | Some(L_BRACKET) | Some(L_ANGLE) => {
189 self.skip_ws();
190 }
191 e => {
192 self.skip_ws();
193 self.error(format!(
194 "Expected ':' or '|' or '[' or '<' or ',' but got {:?}",
195 e
196 ));
197 }
198 }
199
200 if self.peek_past_ws() == Some(L_PARENS) {
201 self.skip_ws();
202 self.builder.start_node(VERSION.into());
203 self.bump();
204 self.skip_ws();
205
206 self.builder.start_node(CONSTRAINT.into());
207
208 while self.current() == Some(L_ANGLE)
209 || self.current() == Some(R_ANGLE)
210 || self.current() == Some(EQUAL)
211 {
212 self.bump();
213 }
214
215 self.builder.finish_node();
216
217 self.skip_ws();
218
219 if self.current() == Some(IDENT) {
220 self.bump();
221 } else {
222 self.error("Expected version".to_string());
223 }
224
225 if self.current() == Some(R_PARENS) {
226 self.bump();
227 } else {
228 self.error("Expected ')'".to_string());
229 }
230
231 self.builder.finish_node();
232 }
233
234 if self.peek_past_ws() == Some(L_BRACKET) {
235 self.skip_ws();
236 self.builder.start_node(ARCHITECTURES.into());
237 self.bump();
238 loop {
239 self.skip_ws();
240 match self.current() {
241 Some(NOT) => {
242 self.bump();
243 }
244 Some(IDENT) => {
245 self.bump();
246 }
247 Some(R_BRACKET) => {
248 self.bump();
249 break;
250 }
251 _ => {
252 self.error("Expected architecture name or '!' or ']'".to_string());
253 }
254 }
255 }
256 self.builder.finish_node();
257 }
258
259 while self.peek_past_ws() == Some(L_ANGLE) {
260 self.skip_ws();
261 self.builder.start_node(PROFILES.into());
262 self.bump();
263
264 loop {
265 self.skip_ws();
266 match self.current() {
267 Some(IDENT) => {
268 self.bump();
269 }
270 Some(NOT) => {
271 self.bump();
272 self.skip_ws();
273 if self.current() == Some(IDENT) {
274 self.bump();
275 } else {
276 self.error("Expected profile".to_string());
277 }
278 }
279 Some(R_ANGLE) => {
280 self.bump();
281 break;
282 }
283 None => {
284 self.error("Expected profile or '>'".to_string());
285 break;
286 }
287 _ => {
288 self.error("Expected profile or '!' or '>'".to_string());
289 }
290 }
291 }
292
293 self.builder.finish_node();
294 }
295
296 self.builder.finish_node();
297 }
298
299 fn parse(mut self) -> Parse {
300 self.builder.start_node(SyntaxKind::ROOT.into());
301
302 self.skip_ws();
303
304 while self.current().is_some() {
305 match self.current() {
306 Some(IDENT) => self.parse_entry(),
307 Some(DOLLAR) => {
308 if self.allow_substvar {
309 self.parse_substvar()
310 } else {
311 self.error("Substvars are not allowed".to_string());
312 }
313 }
314 Some(COMMA) => {
315 }
317 Some(c) => {
318 self.error(format!("expected $ or identifier but got {:?}", c));
319 }
320 None => {
321 self.error("expected identifier but got end of file".to_string());
322 }
323 }
324
325 self.skip_ws();
326 match self.current() {
327 Some(COMMA) => {
328 self.bump();
329 }
330 None => {
331 break;
332 }
333 c => {
334 self.error(format!("expected comma or end of file but got {:?}", c));
335 }
336 }
337 self.skip_ws();
338 }
339
340 self.builder.finish_node();
341 Parse {
343 green_node: self.builder.finish(),
344 errors: self.errors,
345 }
346 }
347 fn bump(&mut self) {
349 let (kind, text) = self.tokens.pop().unwrap();
350 self.builder.token(kind.into(), text.as_str());
351 }
352 fn current(&self) -> Option<SyntaxKind> {
354 self.tokens.last().map(|(kind, _)| *kind)
355 }
356 fn skip_ws(&mut self) {
357 while self.current() == Some(WHITESPACE) || self.current() == Some(NEWLINE) {
358 self.bump()
359 }
360 }
361
362 fn peek_past_ws(&self) -> Option<SyntaxKind> {
363 let mut i = self.tokens.len();
364 while i > 0 {
365 i -= 1;
366 match self.tokens[i].0 {
367 WHITESPACE | NEWLINE => {}
368 _ => return Some(self.tokens[i].0),
369 }
370 }
371 None
372 }
373 }
374
375 let mut tokens = crate::relations::lex(text);
376 tokens.reverse();
377 Parser {
378 tokens,
379 builder: GreenNodeBuilder::new(),
380 errors: Vec::new(),
381 allow_substvar,
382 }
383 .parse()
384}
385
386type SyntaxNode = rowan::SyntaxNode<Lang>;
393#[allow(unused)]
394type SyntaxToken = rowan::SyntaxToken<Lang>;
395#[allow(unused)]
396type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;
397
398impl Parse {
399 fn root_mut(&self) -> Relations {
400 Relations::cast(SyntaxNode::new_root_mut(self.green_node.clone())).unwrap()
401 }
402}
403
404macro_rules! ast_node {
405 ($ast:ident, $kind:ident) => {
406 #[repr(transparent)]
408 pub struct $ast(SyntaxNode);
409 impl $ast {
410 #[allow(unused)]
411 fn cast(node: SyntaxNode) -> Option<Self> {
412 if node.kind() == $kind {
413 Some(Self(node))
414 } else {
415 None
416 }
417 }
418 }
419
420 impl std::fmt::Display for $ast {
421 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
422 f.write_str(&self.0.text().to_string())
423 }
424 }
425 };
426}
427
428ast_node!(Relations, ROOT);
429ast_node!(Entry, ENTRY);
430ast_node!(Relation, RELATION);
431ast_node!(Substvar, SUBSTVAR);
432
433impl PartialEq for Relations {
434 fn eq(&self, other: &Self) -> bool {
435 self.entries().collect::<Vec<_>>() == other.entries().collect::<Vec<_>>()
436 }
437}
438
439impl PartialEq for Entry {
440 fn eq(&self, other: &Self) -> bool {
441 self.relations().collect::<Vec<_>>() == other.relations().collect::<Vec<_>>()
442 }
443}
444
445impl PartialEq for Relation {
446 fn eq(&self, other: &Self) -> bool {
447 self.name() == other.name()
448 && self.version() == other.version()
449 && self.archqual() == other.archqual()
450 && self.architectures().map(|x| x.collect::<HashSet<_>>())
451 == other.architectures().map(|x| x.collect::<HashSet<_>>())
452 && self.profiles().eq(other.profiles())
453 }
454}
455
456#[cfg(feature = "serde")]
457impl serde::Serialize for Relations {
458 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
459 let rep = self.to_string();
460 serializer.serialize_str(&rep)
461 }
462}
463
464#[cfg(feature = "serde")]
465impl<'de> serde::Deserialize<'de> for Relations {
466 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
467 let s = String::deserialize(deserializer)?;
468 let relations = s.parse().map_err(serde::de::Error::custom)?;
469 Ok(relations)
470 }
471}
472
473impl std::fmt::Debug for Relations {
474 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
475 let mut s = f.debug_struct("Relations");
476
477 for entry in self.entries() {
478 s.field("entry", &entry);
479 }
480
481 s.finish()
482 }
483}
484
485impl std::fmt::Debug for Entry {
486 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
487 let mut s = f.debug_struct("Entry");
488
489 for relation in self.relations() {
490 s.field("relation", &relation);
491 }
492
493 s.finish()
494 }
495}
496
497#[cfg(feature = "serde")]
498impl serde::Serialize for Entry {
499 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
500 let rep = self.to_string();
501 serializer.serialize_str(&rep)
502 }
503}
504
505#[cfg(feature = "serde")]
506impl<'de> serde::Deserialize<'de> for Entry {
507 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
508 let s = String::deserialize(deserializer)?;
509 let entry = s.parse().map_err(serde::de::Error::custom)?;
510 Ok(entry)
511 }
512}
513
514impl std::fmt::Debug for Relation {
515 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
516 let mut s = f.debug_struct("Relation");
517
518 s.field("name", &self.name());
519
520 if let Some((vc, version)) = self.version() {
521 s.field("version", &vc);
522 s.field("version", &version);
523 }
524
525 s.finish()
526 }
527}
528
529#[cfg(feature = "serde")]
530impl serde::Serialize for Relation {
531 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
532 let rep = self.to_string();
533 serializer.serialize_str(&rep)
534 }
535}
536
537#[cfg(feature = "serde")]
538impl<'de> serde::Deserialize<'de> for Relation {
539 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
540 let s = String::deserialize(deserializer)?;
541 let relation = s.parse().map_err(serde::de::Error::custom)?;
542 Ok(relation)
543 }
544}
545
546impl Default for Relations {
547 fn default() -> Self {
548 Self::new()
549 }
550}
551
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
2099impl Relation {
2100 pub fn new(name: &str, version_constraint: Option<(VersionConstraint, Version)>) -> Self {
2114 let mut builder = GreenNodeBuilder::new();
2115 builder.start_node(SyntaxKind::RELATION.into());
2116 builder.token(IDENT.into(), name);
2117 if let Some((vc, version)) = version_constraint {
2118 builder.token(WHITESPACE.into(), " ");
2119 builder.start_node(SyntaxKind::VERSION.into());
2120 builder.token(L_PARENS.into(), "(");
2121 builder.start_node(SyntaxKind::CONSTRAINT.into());
2122 for c in vc.to_string().chars() {
2123 builder.token(
2124 match c {
2125 '>' => R_ANGLE.into(),
2126 '<' => L_ANGLE.into(),
2127 '=' => EQUAL.into(),
2128 _ => unreachable!(),
2129 },
2130 c.to_string().as_str(),
2131 );
2132 }
2133 builder.finish_node();
2134
2135 builder.token(WHITESPACE.into(), " ");
2136
2137 builder.token(IDENT.into(), version.to_string().as_str());
2138
2139 builder.token(R_PARENS.into(), ")");
2140
2141 builder.finish_node();
2142 }
2143
2144 builder.finish_node();
2145 Relation(SyntaxNode::new_root_mut(builder.finish()))
2146 }
2147
2148 #[must_use]
2157 pub fn wrap_and_sort(&self) -> Self {
2158 let mut builder = GreenNodeBuilder::new();
2159 builder.start_node(SyntaxKind::RELATION.into());
2160 builder.token(IDENT.into(), self.name().as_str());
2161 if let Some(archqual) = self.archqual() {
2162 builder.token(COLON.into(), ":");
2163 builder.token(IDENT.into(), archqual.as_str());
2164 }
2165 if let Some((vc, version)) = self.version() {
2166 builder.token(WHITESPACE.into(), " ");
2167 builder.start_node(SyntaxKind::VERSION.into());
2168 builder.token(L_PARENS.into(), "(");
2169 builder.start_node(SyntaxKind::CONSTRAINT.into());
2170 builder.token(
2171 match vc {
2172 VersionConstraint::GreaterThanEqual => R_ANGLE.into(),
2173 VersionConstraint::LessThanEqual => L_ANGLE.into(),
2174 VersionConstraint::Equal => EQUAL.into(),
2175 VersionConstraint::GreaterThan => R_ANGLE.into(),
2176 VersionConstraint::LessThan => L_ANGLE.into(),
2177 },
2178 vc.to_string().as_str(),
2179 );
2180 builder.finish_node();
2181 builder.token(WHITESPACE.into(), " ");
2182 builder.token(IDENT.into(), version.to_string().as_str());
2183 builder.token(R_PARENS.into(), ")");
2184 builder.finish_node();
2185 }
2186 if let Some(architectures) = self.architectures() {
2187 builder.token(WHITESPACE.into(), " ");
2188 builder.start_node(ARCHITECTURES.into());
2189 builder.token(L_BRACKET.into(), "[");
2190 for (i, arch) in architectures.enumerate() {
2191 if i > 0 {
2192 builder.token(WHITESPACE.into(), " ");
2193 }
2194 builder.token(IDENT.into(), arch.as_str());
2195 }
2196 builder.token(R_BRACKET.into(), "]");
2197 builder.finish_node();
2198 }
2199 for profiles in self.profiles() {
2200 builder.token(WHITESPACE.into(), " ");
2201 builder.start_node(PROFILES.into());
2202 builder.token(L_ANGLE.into(), "<");
2203 for (i, profile) in profiles.into_iter().enumerate() {
2204 if i > 0 {
2205 builder.token(WHITESPACE.into(), " ");
2206 }
2207 match profile {
2208 BuildProfile::Disabled(name) => {
2209 builder.token(NOT.into(), "!");
2210 builder.token(IDENT.into(), name.as_str());
2211 }
2212 BuildProfile::Enabled(name) => {
2213 builder.token(IDENT.into(), name.as_str());
2214 }
2215 }
2216 }
2217 builder.token(R_ANGLE.into(), ">");
2218 builder.finish_node();
2219 }
2220 builder.finish_node();
2221 Relation(SyntaxNode::new_root_mut(builder.finish()))
2222 }
2223
2224 pub fn simple(name: &str) -> Self {
2233 Self::new(name, None)
2234 }
2235
2236 pub fn drop_constraint(&mut self) -> bool {
2247 let version_token = self.0.children().find(|n| n.kind() == VERSION);
2248 if let Some(version_token) = version_token {
2249 while let Some(prev) = version_token.prev_sibling_or_token() {
2251 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
2252 prev.detach();
2253 } else {
2254 break;
2255 }
2256 }
2257 version_token.detach();
2258 return true;
2259 }
2260
2261 false
2262 }
2263
2264 pub fn name(&self) -> String {
2273 self.0
2274 .children_with_tokens()
2275 .find_map(|it| match it {
2276 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2277 _ => None,
2278 })
2279 .unwrap()
2280 .text()
2281 .to_string()
2282 }
2283
2284 pub fn archqual(&self) -> Option<String> {
2293 let archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
2294 let node = if let Some(archqual) = archqual {
2295 archqual.children_with_tokens().find_map(|it| match it {
2296 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2297 _ => None,
2298 })
2299 } else {
2300 None
2301 };
2302 node.map(|n| n.text().to_string())
2303 }
2304
2305 pub fn set_archqual(&mut self, archqual: &str) {
2315 let mut builder = GreenNodeBuilder::new();
2316 builder.start_node(ARCHQUAL.into());
2317 builder.token(COLON.into(), ":");
2318 builder.token(IDENT.into(), archqual);
2319 builder.finish_node();
2320
2321 let node_archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
2322 if let Some(node_archqual) = node_archqual {
2323 self.0.splice_children(
2324 node_archqual.index()..node_archqual.index() + 1,
2325 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2326 );
2327 } else {
2328 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
2329 let idx = if let Some(name_node) = name_node {
2330 name_node.index() + 1
2331 } else {
2332 0
2333 };
2334 self.0.splice_children(
2335 idx..idx,
2336 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2337 );
2338 }
2339 }
2340
2341 pub fn version(&self) -> Option<(VersionConstraint, Version)> {
2343 let vc = self.0.children().find(|n| n.kind() == VERSION);
2344 let vc = vc.as_ref()?;
2345 let constraint = vc.children().find(|n| n.kind() == CONSTRAINT);
2346
2347 let version = vc.children_with_tokens().find_map(|it| match it {
2348 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2349 _ => None,
2350 });
2351
2352 if let (Some(constraint), Some(version)) = (constraint, version) {
2353 let vc: VersionConstraint = constraint.to_string().parse().unwrap();
2354 Some((vc, (version.text().to_string()).parse().unwrap()))
2355 } else {
2356 None
2357 }
2358 }
2359
2360 pub fn set_version(&mut self, version_constraint: Option<(VersionConstraint, Version)>) {
2371 let current_version = self.0.children().find(|n| n.kind() == VERSION);
2372 if let Some((vc, version)) = version_constraint {
2373 let mut builder = GreenNodeBuilder::new();
2374 builder.start_node(VERSION.into());
2375 builder.token(L_PARENS.into(), "(");
2376 builder.start_node(CONSTRAINT.into());
2377 match vc {
2378 VersionConstraint::GreaterThanEqual => {
2379 builder.token(R_ANGLE.into(), ">");
2380 builder.token(EQUAL.into(), "=");
2381 }
2382 VersionConstraint::LessThanEqual => {
2383 builder.token(L_ANGLE.into(), "<");
2384 builder.token(EQUAL.into(), "=");
2385 }
2386 VersionConstraint::Equal => {
2387 builder.token(EQUAL.into(), "=");
2388 }
2389 VersionConstraint::GreaterThan => {
2390 builder.token(R_ANGLE.into(), ">");
2391 }
2392 VersionConstraint::LessThan => {
2393 builder.token(L_ANGLE.into(), "<");
2394 }
2395 }
2396 builder.finish_node(); builder.token(WHITESPACE.into(), " ");
2398 builder.token(IDENT.into(), version.to_string().as_str());
2399 builder.token(R_PARENS.into(), ")");
2400 builder.finish_node(); if let Some(current_version) = current_version {
2403 self.0.splice_children(
2404 current_version.index()..current_version.index() + 1,
2405 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2406 );
2407 } else {
2408 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
2409 let idx = if let Some(name_node) = name_node {
2410 name_node.index() + 1
2411 } else {
2412 0
2413 };
2414 let new_children = vec![
2415 GreenToken::new(WHITESPACE.into(), " ").into(),
2416 builder.finish().into(),
2417 ];
2418 let new_root = SyntaxNode::new_root_mut(
2419 self.0.green().splice_children(idx..idx, new_children),
2420 );
2421 if let Some(parent) = self.0.parent() {
2422 parent
2423 .splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2424 self.0 = parent
2425 .children_with_tokens()
2426 .nth(self.0.index())
2427 .unwrap()
2428 .clone()
2429 .into_node()
2430 .unwrap();
2431 } else {
2432 self.0 = new_root;
2433 }
2434 }
2435 } else if let Some(current_version) = current_version {
2436 while let Some(prev) = current_version.prev_sibling_or_token() {
2438 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
2439 prev.detach();
2440 } else {
2441 break;
2442 }
2443 }
2444 current_version.detach();
2445 }
2446 }
2447
2448 pub fn architectures(&self) -> Option<impl Iterator<Item = String> + '_> {
2457 let architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES)?;
2458
2459 Some(architectures.children_with_tokens().filter_map(|node| {
2460 let token = node.as_token()?;
2461 if token.kind() == IDENT {
2462 Some(token.text().to_string())
2463 } else {
2464 None
2465 }
2466 }))
2467 }
2468
2469 pub fn profiles(&self) -> impl Iterator<Item = Vec<BuildProfile>> + '_ {
2479 let profiles = self.0.children().filter(|n| n.kind() == PROFILES);
2480
2481 profiles.map(|profile| {
2482 let mut ret = vec![];
2484 let mut current = vec![];
2485 for token in profile.children_with_tokens() {
2486 match token.kind() {
2487 WHITESPACE | NEWLINE => {
2488 if !current.is_empty() {
2489 ret.push(current.join("").parse::<BuildProfile>().unwrap());
2490 current = vec![];
2491 }
2492 }
2493 L_ANGLE | R_ANGLE => {}
2494 _ => {
2495 current.push(token.to_string());
2496 }
2497 }
2498 }
2499 if !current.is_empty() {
2500 ret.push(current.concat().parse().unwrap());
2501 }
2502 ret
2503 })
2504 }
2505
2506 pub fn remove(&mut self) {
2517 let is_first = !self
2518 .0
2519 .siblings(Direction::Prev)
2520 .skip(1)
2521 .any(|n| n.kind() == RELATION);
2522 if !is_first {
2523 while let Some(n) = self.0.prev_sibling_or_token() {
2526 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2527 n.detach();
2528 } else if n.kind() == PIPE {
2529 n.detach();
2530 break;
2531 } else {
2532 break;
2533 }
2534 }
2535 while let Some(n) = self.0.prev_sibling_or_token() {
2536 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2537 n.detach();
2538 } else {
2539 break;
2540 }
2541 }
2542 } else {
2543 while let Some(n) = self.0.next_sibling_or_token() {
2546 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2547 n.detach();
2548 } else if n.kind() == PIPE {
2549 n.detach();
2550 break;
2551 } else {
2552 panic!("Unexpected node: {:?}", n);
2553 }
2554 }
2555
2556 while let Some(n) = self.0.next_sibling_or_token() {
2557 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2558 n.detach();
2559 } else {
2560 break;
2561 }
2562 }
2563 }
2564 if let Some(mut parent) = self.0.parent().and_then(Entry::cast) {
2566 if parent.is_empty() {
2567 parent.remove();
2568 } else {
2569 self.0.detach();
2570 }
2571 } else {
2572 self.0.detach();
2573 }
2574 }
2575
2576 pub fn set_architectures<'a>(&mut self, architectures: impl Iterator<Item = &'a str>) {
2586 let mut builder = GreenNodeBuilder::new();
2587 builder.start_node(ARCHITECTURES.into());
2588 builder.token(L_BRACKET.into(), "[");
2589 for (i, arch) in architectures.enumerate() {
2590 if i > 0 {
2591 builder.token(WHITESPACE.into(), " ");
2592 }
2593 builder.token(IDENT.into(), arch);
2594 }
2595 builder.token(R_BRACKET.into(), "]");
2596 builder.finish_node();
2597
2598 let node_architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES);
2599 if let Some(node_architectures) = node_architectures {
2600 let new_root = SyntaxNode::new_root_mut(builder.finish());
2601 self.0.splice_children(
2602 node_architectures.index()..node_architectures.index() + 1,
2603 vec![new_root.into()],
2604 );
2605 } else {
2606 let profiles = self.0.children().find(|n| n.kind() == PROFILES);
2607 let idx = if let Some(profiles) = profiles {
2608 profiles.index()
2609 } else {
2610 self.0.children_with_tokens().count()
2611 };
2612 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
2613 idx..idx,
2614 vec![
2615 GreenToken::new(WHITESPACE.into(), " ").into(),
2616 builder.finish().into(),
2617 ],
2618 ));
2619 if let Some(parent) = self.0.parent() {
2620 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2621 self.0 = parent
2622 .children_with_tokens()
2623 .nth(self.0.index())
2624 .unwrap()
2625 .clone()
2626 .into_node()
2627 .unwrap();
2628 } else {
2629 self.0 = new_root;
2630 }
2631 }
2632 }
2633
2634 pub fn add_profile(&mut self, profile: &[BuildProfile]) {
2645 let mut builder = GreenNodeBuilder::new();
2646 builder.start_node(PROFILES.into());
2647 builder.token(L_ANGLE.into(), "<");
2648 for (i, profile) in profile.iter().enumerate() {
2649 if i > 0 {
2650 builder.token(WHITESPACE.into(), " ");
2651 }
2652 match profile {
2653 BuildProfile::Disabled(name) => {
2654 builder.token(NOT.into(), "!");
2655 builder.token(IDENT.into(), name.as_str());
2656 }
2657 BuildProfile::Enabled(name) => {
2658 builder.token(IDENT.into(), name.as_str());
2659 }
2660 }
2661 }
2662 builder.token(R_ANGLE.into(), ">");
2663 builder.finish_node();
2664
2665 let node_profiles = self.0.children().find(|n| n.kind() == PROFILES);
2666 if let Some(node_profiles) = node_profiles {
2667 let new_root = SyntaxNode::new_root_mut(builder.finish());
2668 self.0.splice_children(
2669 node_profiles.index()..node_profiles.index() + 1,
2670 vec![new_root.into()],
2671 );
2672 } else {
2673 let idx = self.0.children_with_tokens().count();
2674 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
2675 idx..idx,
2676 vec![
2677 GreenToken::new(WHITESPACE.into(), " ").into(),
2678 builder.finish().into(),
2679 ],
2680 ));
2681 if let Some(parent) = self.0.parent() {
2682 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2683 self.0 = parent
2684 .children_with_tokens()
2685 .nth(self.0.index())
2686 .unwrap()
2687 .clone()
2688 .into_node()
2689 .unwrap();
2690 } else {
2691 self.0 = new_root;
2692 }
2693 }
2694 }
2695
2696 pub fn build(name: &str) -> RelationBuilder {
2698 RelationBuilder::new(name)
2699 }
2700
2701 pub fn is_implied_by(&self, outer: &Relation) -> bool {
2728 if self.name() != outer.name() {
2729 return false;
2730 }
2731
2732 let inner_version = self.version();
2733 let outer_version = outer.version();
2734
2735 if inner_version.is_none() {
2737 return true;
2738 }
2739
2740 if inner_version == outer_version {
2742 return true;
2743 }
2744
2745 if outer_version.is_none() {
2747 return false;
2748 }
2749
2750 let (inner_constraint, inner_ver) = inner_version.unwrap();
2751 let (outer_constraint, outer_ver) = outer_version.unwrap();
2752
2753 use VersionConstraint::*;
2754 match inner_constraint {
2755 GreaterThanEqual => match outer_constraint {
2756 GreaterThan => outer_ver > inner_ver,
2757 GreaterThanEqual | Equal => outer_ver >= inner_ver,
2758 LessThan | LessThanEqual => false,
2759 },
2760 Equal => match outer_constraint {
2761 Equal => outer_ver == inner_ver,
2762 _ => false,
2763 },
2764 LessThan => match outer_constraint {
2765 LessThan => outer_ver <= inner_ver,
2766 LessThanEqual | Equal => outer_ver < inner_ver,
2767 GreaterThan | GreaterThanEqual => false,
2768 },
2769 LessThanEqual => match outer_constraint {
2770 LessThanEqual | Equal | LessThan => outer_ver <= inner_ver,
2771 GreaterThan | GreaterThanEqual => false,
2772 },
2773 GreaterThan => match outer_constraint {
2774 GreaterThan => outer_ver >= inner_ver,
2775 Equal | GreaterThanEqual => outer_ver > inner_ver,
2776 LessThan | LessThanEqual => false,
2777 },
2778 }
2779 }
2780}
2781
2782pub struct RelationBuilder {
2796 name: String,
2797 version_constraint: Option<(VersionConstraint, Version)>,
2798 archqual: Option<String>,
2799 architectures: Option<Vec<String>>,
2800 profiles: Vec<Vec<BuildProfile>>,
2801}
2802
2803impl RelationBuilder {
2804 fn new(name: &str) -> Self {
2806 Self {
2807 name: name.to_string(),
2808 version_constraint: None,
2809 archqual: None,
2810 architectures: None,
2811 profiles: vec![],
2812 }
2813 }
2814
2815 pub fn version_constraint(mut self, vc: VersionConstraint, version: Version) -> Self {
2817 self.version_constraint = Some((vc, version));
2818 self
2819 }
2820
2821 pub fn archqual(mut self, archqual: &str) -> Self {
2823 self.archqual = Some(archqual.to_string());
2824 self
2825 }
2826
2827 pub fn architectures(mut self, architectures: Vec<String>) -> Self {
2829 self.architectures = Some(architectures);
2830 self
2831 }
2832
2833 pub fn profiles(mut self, profiles: Vec<Vec<BuildProfile>>) -> Self {
2835 self.profiles = profiles;
2836 self
2837 }
2838
2839 pub fn add_profile(mut self, profile: Vec<BuildProfile>) -> Self {
2841 self.profiles.push(profile);
2842 self
2843 }
2844
2845 pub fn build(self) -> Relation {
2847 let mut relation = Relation::new(&self.name, self.version_constraint);
2848 if let Some(archqual) = &self.archqual {
2849 relation.set_archqual(archqual);
2850 }
2851 if let Some(architectures) = &self.architectures {
2852 relation.set_architectures(architectures.iter().map(|s| s.as_str()));
2853 }
2854 for profile in &self.profiles {
2855 relation.add_profile(profile);
2856 }
2857 relation
2858 }
2859}
2860
2861impl PartialOrd for Relation {
2862 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
2863 let name_cmp = self.name().cmp(&other.name());
2865 if name_cmp != std::cmp::Ordering::Equal {
2866 return Some(name_cmp);
2867 }
2868
2869 let self_version = self.version();
2870 let other_version = other.version();
2871
2872 match (self_version, other_version) {
2873 (Some((self_vc, self_version)), Some((other_vc, other_version))) => {
2874 let vc_cmp = self_vc.cmp(&other_vc);
2875 if vc_cmp != std::cmp::Ordering::Equal {
2876 return Some(vc_cmp);
2877 }
2878
2879 Some(self_version.cmp(&other_version))
2880 }
2881 (Some(_), None) => Some(std::cmp::Ordering::Greater),
2882 (None, Some(_)) => Some(std::cmp::Ordering::Less),
2883 (None, None) => Some(std::cmp::Ordering::Equal),
2884 }
2885 }
2886}
2887
2888impl Eq for Relation {}
2889
2890impl Ord for Relation {
2891 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
2892 self.partial_cmp(other).unwrap()
2893 }
2894}
2895
2896impl std::str::FromStr for Relations {
2897 type Err = String;
2898
2899 fn from_str(s: &str) -> Result<Self, Self::Err> {
2900 let parse = parse(s, false);
2901 if parse.errors.is_empty() {
2902 Ok(parse.root_mut())
2903 } else {
2904 Err(parse.errors.join("\n"))
2905 }
2906 }
2907}
2908
2909impl std::str::FromStr for Entry {
2910 type Err = String;
2911
2912 fn from_str(s: &str) -> Result<Self, Self::Err> {
2913 let root: Relations = s.parse()?;
2914
2915 let mut entries = root.entries();
2916 let entry = if let Some(entry) = entries.next() {
2917 entry
2918 } else {
2919 return Err("No entry found".to_string());
2920 };
2921
2922 if entries.next().is_some() {
2923 return Err("Multiple entries found".to_string());
2924 }
2925
2926 Ok(entry)
2927 }
2928}
2929
2930impl std::str::FromStr for Relation {
2931 type Err = String;
2932
2933 fn from_str(s: &str) -> Result<Self, Self::Err> {
2934 let entry: Entry = s.parse()?;
2935
2936 let mut relations = entry.relations();
2937 let relation = if let Some(relation) = relations.next() {
2938 relation
2939 } else {
2940 return Err("No relation found".to_string());
2941 };
2942
2943 if relations.next().is_some() {
2944 return Err("Multiple relations found".to_string());
2945 }
2946
2947 Ok(relation)
2948 }
2949}
2950
2951impl From<crate::lossy::Relation> for Relation {
2952 fn from(relation: crate::lossy::Relation) -> Self {
2953 let mut builder = Relation::build(&relation.name);
2954
2955 if let Some((vc, version)) = relation.version {
2956 builder = builder.version_constraint(vc, version);
2957 }
2958
2959 if let Some(archqual) = relation.archqual {
2960 builder = builder.archqual(&archqual);
2961 }
2962
2963 if let Some(architectures) = relation.architectures {
2964 builder = builder.architectures(architectures);
2965 }
2966
2967 builder = builder.profiles(relation.profiles);
2968
2969 builder.build()
2970 }
2971}
2972
2973impl From<Relation> for crate::lossy::Relation {
2974 fn from(relation: Relation) -> Self {
2975 crate::lossy::Relation {
2976 name: relation.name(),
2977 version: relation.version(),
2978 archqual: relation.archqual(),
2979 architectures: relation.architectures().map(|a| a.collect()),
2980 profiles: relation.profiles().collect(),
2981 }
2982 }
2983}
2984
2985impl From<Entry> for Vec<crate::lossy::Relation> {
2986 fn from(entry: Entry) -> Self {
2987 entry.relations().map(|r| r.into()).collect()
2988 }
2989}
2990
2991impl From<Vec<crate::lossy::Relation>> for Entry {
2992 fn from(relations: Vec<crate::lossy::Relation>) -> Self {
2993 let relations: Vec<Relation> = relations.into_iter().map(|r| r.into()).collect();
2994 Entry::from(relations)
2995 }
2996}
2997
2998#[cfg(test)]
2999mod tests {
3000 use super::*;
3001
3002 #[test]
3003 fn test_parse() {
3004 let input = "python3-dulwich";
3005 let parsed: Relations = input.parse().unwrap();
3006 assert_eq!(parsed.to_string(), input);
3007 assert_eq!(parsed.entries().count(), 1);
3008 let entry = parsed.entries().next().unwrap();
3009 assert_eq!(entry.to_string(), "python3-dulwich");
3010 assert_eq!(entry.relations().count(), 1);
3011 let relation = entry.relations().next().unwrap();
3012 assert_eq!(relation.to_string(), "python3-dulwich");
3013 assert_eq!(relation.version(), None);
3014
3015 let input = "python3-dulwich (>= 0.20.21)";
3016 let parsed: Relations = input.parse().unwrap();
3017 assert_eq!(parsed.to_string(), input);
3018 assert_eq!(parsed.entries().count(), 1);
3019 let entry = parsed.entries().next().unwrap();
3020 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3021 assert_eq!(entry.relations().count(), 1);
3022 let relation = entry.relations().next().unwrap();
3023 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
3024 assert_eq!(
3025 relation.version(),
3026 Some((
3027 VersionConstraint::GreaterThanEqual,
3028 "0.20.21".parse().unwrap()
3029 ))
3030 );
3031 }
3032
3033 #[test]
3034 fn test_multiple() {
3035 let input = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)";
3036 let parsed: Relations = input.parse().unwrap();
3037 assert_eq!(parsed.to_string(), input);
3038 assert_eq!(parsed.entries().count(), 2);
3039 let entry = parsed.entries().next().unwrap();
3040 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3041 assert_eq!(entry.relations().count(), 1);
3042 let relation = entry.relations().next().unwrap();
3043 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
3044 assert_eq!(
3045 relation.version(),
3046 Some((
3047 VersionConstraint::GreaterThanEqual,
3048 "0.20.21".parse().unwrap()
3049 ))
3050 );
3051 let entry = parsed.entries().nth(1).unwrap();
3052 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.21)");
3053 assert_eq!(entry.relations().count(), 1);
3054 let relation = entry.relations().next().unwrap();
3055 assert_eq!(relation.to_string(), "python3-dulwich (<< 0.21)");
3056 assert_eq!(
3057 relation.version(),
3058 Some((VersionConstraint::LessThan, "0.21".parse().unwrap()))
3059 );
3060 }
3061
3062 #[test]
3063 fn test_architectures() {
3064 let input = "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]";
3065 let parsed: Relations = input.parse().unwrap();
3066 assert_eq!(parsed.to_string(), input);
3067 assert_eq!(parsed.entries().count(), 1);
3068 let entry = parsed.entries().next().unwrap();
3069 assert_eq!(
3070 entry.to_string(),
3071 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
3072 );
3073 assert_eq!(entry.relations().count(), 1);
3074 let relation = entry.relations().next().unwrap();
3075 assert_eq!(
3076 relation.to_string(),
3077 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
3078 );
3079 assert_eq!(relation.version(), None);
3080 assert_eq!(
3081 relation.architectures().unwrap().collect::<Vec<_>>(),
3082 vec![
3083 "amd64", "arm64", "armhf", "i386", "mips", "mips64el", "mipsel", "ppc64el", "s390x"
3084 ]
3085 .into_iter()
3086 .map(|s| s.to_string())
3087 .collect::<Vec<_>>()
3088 );
3089 }
3090
3091 #[test]
3092 fn test_profiles() {
3093 let input = "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>, bar";
3094 let parsed: Relations = input.parse().unwrap();
3095 assert_eq!(parsed.to_string(), input);
3096 assert_eq!(parsed.entries().count(), 2);
3097 let entry = parsed.entries().next().unwrap();
3098 assert_eq!(
3099 entry.to_string(),
3100 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
3101 );
3102 assert_eq!(entry.relations().count(), 1);
3103 let relation = entry.relations().next().unwrap();
3104 assert_eq!(
3105 relation.to_string(),
3106 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
3107 );
3108 assert_eq!(
3109 relation.version(),
3110 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap()))
3111 );
3112 assert_eq!(
3113 relation.architectures().unwrap().collect::<Vec<_>>(),
3114 vec!["i386", "arm"]
3115 .into_iter()
3116 .map(|s| s.to_string())
3117 .collect::<Vec<_>>()
3118 );
3119 assert_eq!(
3120 relation.profiles().collect::<Vec<_>>(),
3121 vec![
3122 vec![BuildProfile::Disabled("nocheck".to_string())],
3123 vec![BuildProfile::Disabled("cross".to_string())]
3124 ]
3125 );
3126 }
3127
3128 #[test]
3129 fn test_substvar() {
3130 let input = "${shlibs:Depends}";
3131
3132 let (parsed, errors) = Relations::parse_relaxed(input, true);
3133 assert_eq!(errors, Vec::<String>::new());
3134 assert_eq!(parsed.to_string(), input);
3135 assert_eq!(parsed.entries().count(), 0);
3136
3137 assert_eq!(
3138 parsed.substvars().collect::<Vec<_>>(),
3139 vec!["${shlibs:Depends}"]
3140 );
3141 }
3142
3143 #[test]
3144 fn test_new() {
3145 let r = Relation::new(
3146 "samba",
3147 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
3148 );
3149
3150 assert_eq!(r.to_string(), "samba (>= 2.0)");
3151 }
3152
3153 #[test]
3154 fn test_drop_constraint() {
3155 let mut r = Relation::new(
3156 "samba",
3157 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
3158 );
3159
3160 r.drop_constraint();
3161
3162 assert_eq!(r.to_string(), "samba");
3163 }
3164
3165 #[test]
3166 fn test_simple() {
3167 let r = Relation::simple("samba");
3168
3169 assert_eq!(r.to_string(), "samba");
3170 }
3171
3172 #[test]
3173 fn test_remove_first_entry() {
3174 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3175 .parse()
3176 .unwrap();
3177 let removed = rels.remove_entry(0);
3178 assert_eq!(removed.to_string(), "python3-dulwich (>= 0.20.21)");
3179 assert_eq!(rels.to_string(), "python3-dulwich (<< 0.21)");
3180 }
3181
3182 #[test]
3183 fn test_remove_last_entry() {
3184 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3185 .parse()
3186 .unwrap();
3187 rels.remove_entry(1);
3188 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
3189 }
3190
3191 #[test]
3192 fn test_remove_middle() {
3193 let mut rels: Relations =
3194 r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21), python3-dulwich (<< 0.22)"#
3195 .parse()
3196 .unwrap();
3197 rels.remove_entry(1);
3198 assert_eq!(
3199 rels.to_string(),
3200 "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.22)"
3201 );
3202 }
3203
3204 #[test]
3205 fn test_remove_added() {
3206 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
3207 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3208 rels.push(entry);
3209 rels.remove_entry(1);
3210 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
3211 }
3212
3213 #[test]
3214 fn test_push() {
3215 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
3216 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3217 rels.push(entry);
3218 assert_eq!(
3219 rels.to_string(),
3220 "python3-dulwich (>= 0.20.21), python3-dulwich"
3221 );
3222 }
3223
3224 #[test]
3225 fn test_insert_with_custom_separator() {
3226 let mut rels: Relations = "python3".parse().unwrap();
3227 let entry = Entry::from(vec![Relation::simple("debhelper")]);
3228 rels.insert_with_separator(1, entry, Some("\n "));
3229 assert_eq!(rels.to_string(), "python3,\n debhelper");
3230 }
3231
3232 #[test]
3233 fn test_insert_with_wrap_and_sort_separator() {
3234 let mut rels: Relations = "python3".parse().unwrap();
3235 let entry = Entry::from(vec![Relation::simple("rustc")]);
3236 rels.insert_with_separator(1, entry, Some("\n "));
3238 assert_eq!(rels.to_string(), "python3,\n rustc");
3239 }
3240
3241 #[test]
3242 fn test_push_from_empty() {
3243 let mut rels: Relations = "".parse().unwrap();
3244 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3245 rels.push(entry);
3246 assert_eq!(rels.to_string(), "python3-dulwich");
3247 }
3248
3249 #[test]
3250 fn test_insert() {
3251 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3252 .parse()
3253 .unwrap();
3254 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3255 rels.insert(1, entry);
3256 assert_eq!(
3257 rels.to_string(),
3258 "python3-dulwich (>= 0.20.21), python3-dulwich, python3-dulwich (<< 0.21)"
3259 );
3260 }
3261
3262 #[test]
3263 fn test_insert_at_start() {
3264 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3265 .parse()
3266 .unwrap();
3267 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3268 rels.insert(0, entry);
3269 assert_eq!(
3270 rels.to_string(),
3271 "python3-dulwich, python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3272 );
3273 }
3274
3275 #[test]
3276 fn test_insert_after_error() {
3277 let (mut rels, errors) = Relations::parse_relaxed("@foo@, debhelper (>= 1.0)", false);
3278 assert_eq!(
3279 errors,
3280 vec![
3281 "expected $ or identifier but got ERROR",
3282 "expected comma or end of file but got Some(IDENT)",
3283 "expected $ or identifier but got ERROR"
3284 ]
3285 );
3286 let entry = Entry::from(vec![Relation::simple("bar")]);
3287 rels.push(entry);
3288 assert_eq!(rels.to_string(), "@foo@, debhelper (>= 1.0), bar");
3289 }
3290
3291 #[test]
3292 fn test_insert_before_error() {
3293 let (mut rels, errors) = Relations::parse_relaxed("debhelper (>= 1.0), @foo@, bla", false);
3294 assert_eq!(
3295 errors,
3296 vec![
3297 "expected $ or identifier but got ERROR",
3298 "expected comma or end of file but got Some(IDENT)",
3299 "expected $ or identifier but got ERROR"
3300 ]
3301 );
3302 let entry = Entry::from(vec![Relation::simple("bar")]);
3303 rels.insert(0, entry);
3304 assert_eq!(rels.to_string(), "bar, debhelper (>= 1.0), @foo@, bla");
3305 }
3306
3307 #[test]
3308 fn test_replace() {
3309 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3310 .parse()
3311 .unwrap();
3312 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3313 rels.replace(1, entry);
3314 assert_eq!(
3315 rels.to_string(),
3316 "python3-dulwich (>= 0.20.21), python3-dulwich"
3317 );
3318 }
3319
3320 #[test]
3321 fn test_relation_from_entries() {
3322 let entries = vec![
3323 Entry::from(vec![Relation::simple("python3-dulwich")]),
3324 Entry::from(vec![Relation::simple("python3-breezy")]),
3325 ];
3326 let rels: Relations = entries.into();
3327 assert_eq!(rels.entries().count(), 2);
3328 assert_eq!(rels.to_string(), "python3-dulwich, python3-breezy");
3329 }
3330
3331 #[test]
3332 fn test_entry_from_relations() {
3333 let relations = vec![
3334 Relation::simple("python3-dulwich"),
3335 Relation::simple("python3-breezy"),
3336 ];
3337 let entry: Entry = relations.into();
3338 assert_eq!(entry.relations().count(), 2);
3339 assert_eq!(entry.to_string(), "python3-dulwich | python3-breezy");
3340 }
3341
3342 #[test]
3343 fn test_parse_entry() {
3344 let parsed: Entry = "python3-dulwich (>= 0.20.21) | bar".parse().unwrap();
3345 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21) | bar");
3346 assert_eq!(parsed.relations().count(), 2);
3347
3348 assert_eq!(
3349 "foo, bar".parse::<Entry>().unwrap_err(),
3350 "Multiple entries found"
3351 );
3352 assert_eq!("".parse::<Entry>().unwrap_err(), "No entry found");
3353 }
3354
3355 #[test]
3356 fn test_parse_relation() {
3357 let parsed: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3358 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21)");
3359 assert_eq!(
3360 parsed.version(),
3361 Some((
3362 VersionConstraint::GreaterThanEqual,
3363 "0.20.21".parse().unwrap()
3364 ))
3365 );
3366 assert_eq!(
3367 "foo | bar".parse::<Relation>().unwrap_err(),
3368 "Multiple relations found"
3369 );
3370 assert_eq!("".parse::<Relation>().unwrap_err(), "No entry found");
3371 }
3372
3373 #[test]
3374 fn test_special() {
3375 let parsed: Relation = "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
3376 .parse()
3377 .unwrap();
3378 assert_eq!(
3379 parsed.to_string(),
3380 "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
3381 );
3382 assert_eq!(
3383 parsed.version(),
3384 Some((
3385 VersionConstraint::GreaterThanEqual,
3386 "0.1.138-~~".parse().unwrap()
3387 ))
3388 );
3389 assert_eq!(parsed.archqual(), Some("amd64".to_string()));
3390 assert_eq!(parsed.name(), "librust-breezyshim+dirty-tracker-dev");
3391 }
3392
3393 #[test]
3394 fn test_relations_satisfied_by() {
3395 let rels: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3396 .parse()
3397 .unwrap();
3398 let satisfied = |name: &str| -> Option<debversion::Version> {
3399 match name {
3400 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
3401 _ => None,
3402 }
3403 };
3404 assert!(rels.satisfied_by(satisfied));
3405
3406 let satisfied = |name: &str| match name {
3407 "python3-dulwich" => Some("0.21".parse().unwrap()),
3408 _ => None,
3409 };
3410 assert!(!rels.satisfied_by(satisfied));
3411
3412 let satisfied = |name: &str| match name {
3413 "python3-dulwich" => Some("0.20.20".parse().unwrap()),
3414 _ => None,
3415 };
3416 assert!(!rels.satisfied_by(satisfied));
3417 }
3418
3419 #[test]
3420 fn test_entry_satisfied_by() {
3421 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3422 .parse()
3423 .unwrap();
3424 let satisfied = |name: &str| -> Option<debversion::Version> {
3425 match name {
3426 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
3427 _ => None,
3428 }
3429 };
3430 assert!(entry.satisfied_by(satisfied));
3431 let satisfied = |name: &str| -> Option<debversion::Version> {
3432 match name {
3433 "python3-dulwich" => Some("0.18".parse().unwrap()),
3434 _ => None,
3435 }
3436 };
3437 assert!(!entry.satisfied_by(satisfied));
3438 }
3439
3440 #[test]
3441 fn test_wrap_and_sort_relation() {
3442 let relation: Relation = " python3-dulwich (>= 11) [ amd64 ] < lala>"
3443 .parse()
3444 .unwrap();
3445
3446 let wrapped = relation.wrap_and_sort();
3447
3448 assert_eq!(
3449 wrapped.to_string(),
3450 "python3-dulwich (>= 11) [amd64] <lala>"
3451 );
3452 }
3453
3454 #[test]
3455 fn test_wrap_and_sort_relations() {
3456 let entry: Relations =
3457 "python3-dulwich (>= 0.20.21) | bar, \n\n\n\npython3-dulwich (<< 0.21)"
3458 .parse()
3459 .unwrap();
3460
3461 let wrapped = entry.wrap_and_sort();
3462
3463 assert_eq!(
3464 wrapped.to_string(),
3465 "bar | python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3466 );
3467 }
3468
3469 #[cfg(feature = "serde")]
3470 #[test]
3471 fn test_serialize_relations() {
3472 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3473 .parse()
3474 .unwrap();
3475 let serialized = serde_json::to_string(&relations).unwrap();
3476 assert_eq!(
3477 serialized,
3478 r#""python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)""#
3479 );
3480 }
3481
3482 #[cfg(feature = "serde")]
3483 #[test]
3484 fn test_deserialize_relations() {
3485 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3486 .parse()
3487 .unwrap();
3488 let serialized = serde_json::to_string(&relations).unwrap();
3489 let deserialized: Relations = serde_json::from_str(&serialized).unwrap();
3490 assert_eq!(deserialized.to_string(), relations.to_string());
3491 }
3492
3493 #[cfg(feature = "serde")]
3494 #[test]
3495 fn test_serialize_relation() {
3496 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3497 let serialized = serde_json::to_string(&relation).unwrap();
3498 assert_eq!(serialized, r#""python3-dulwich (>= 0.20.21)""#);
3499 }
3500
3501 #[cfg(feature = "serde")]
3502 #[test]
3503 fn test_deserialize_relation() {
3504 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3505 let serialized = serde_json::to_string(&relation).unwrap();
3506 let deserialized: Relation = serde_json::from_str(&serialized).unwrap();
3507 assert_eq!(deserialized.to_string(), relation.to_string());
3508 }
3509
3510 #[cfg(feature = "serde")]
3511 #[test]
3512 fn test_serialize_entry() {
3513 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3514 .parse()
3515 .unwrap();
3516 let serialized = serde_json::to_string(&entry).unwrap();
3517 assert_eq!(
3518 serialized,
3519 r#""python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)""#
3520 );
3521 }
3522
3523 #[cfg(feature = "serde")]
3524 #[test]
3525 fn test_deserialize_entry() {
3526 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3527 .parse()
3528 .unwrap();
3529 let serialized = serde_json::to_string(&entry).unwrap();
3530 let deserialized: Entry = serde_json::from_str(&serialized).unwrap();
3531 assert_eq!(deserialized.to_string(), entry.to_string());
3532 }
3533
3534 #[test]
3535 fn test_remove_first_relation() {
3536 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3537 .parse()
3538 .unwrap();
3539 let mut rel = entry.relations().next().unwrap();
3540 rel.remove();
3541 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.18)");
3542 }
3543
3544 #[test]
3545 fn test_remove_last_relation() {
3546 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3547 .parse()
3548 .unwrap();
3549 let mut rel = entry.relations().nth(1).unwrap();
3550 rel.remove();
3551 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3552 }
3553
3554 #[test]
3555 fn test_remove_only_relation() {
3556 let entry: Entry = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3557 let mut rel = entry.relations().next().unwrap();
3558 rel.remove();
3559 assert_eq!(entry.to_string(), "");
3560 }
3561
3562 #[test]
3563 fn test_relations_is_empty() {
3564 let entry: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3565 assert!(!entry.is_empty());
3566 assert_eq!(1, entry.len());
3567 let mut rel = entry.entries().next().unwrap();
3568 rel.remove();
3569 assert!(entry.is_empty());
3570 assert_eq!(0, entry.len());
3571 }
3572
3573 #[test]
3574 fn test_entry_is_empty() {
3575 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3576 .parse()
3577 .unwrap();
3578 assert!(!entry.is_empty());
3579 assert_eq!(2, entry.len());
3580 let mut rel = entry.relations().next().unwrap();
3581 rel.remove();
3582 assert!(!entry.is_empty());
3583 assert_eq!(1, entry.len());
3584 let mut rel = entry.relations().next().unwrap();
3585 rel.remove();
3586 assert!(entry.is_empty());
3587 assert_eq!(0, entry.len());
3588 }
3589
3590 #[test]
3591 fn test_relation_set_version() {
3592 let mut rel: Relation = "samba".parse().unwrap();
3593 rel.set_version(None);
3594 assert_eq!("samba", rel.to_string());
3595 rel.set_version(Some((
3596 VersionConstraint::GreaterThanEqual,
3597 "2.0".parse().unwrap(),
3598 )));
3599 assert_eq!("samba (>= 2.0)", rel.to_string());
3600 rel.set_version(None);
3601 assert_eq!("samba", rel.to_string());
3602 rel.set_version(Some((
3603 VersionConstraint::GreaterThanEqual,
3604 "2.0".parse().unwrap(),
3605 )));
3606 rel.set_version(Some((
3607 VersionConstraint::GreaterThanEqual,
3608 "1.1".parse().unwrap(),
3609 )));
3610 assert_eq!("samba (>= 1.1)", rel.to_string());
3611 }
3612
3613 #[test]
3614 fn test_replace_relation() {
3615 let mut entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3616 .parse()
3617 .unwrap();
3618 let new_rel = Relation::simple("python3-breezy");
3619 entry.replace(0, new_rel);
3620 assert_eq!(
3621 entry.to_string(),
3622 "python3-breezy | python3-dulwich (<< 0.18)"
3623 );
3624 }
3625
3626 #[test]
3627 fn test_entry_push_relation() {
3628 let relations: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3629 let new_rel = Relation::simple("python3-breezy");
3630 let mut entry = relations.entries().next().unwrap();
3631 entry.push(new_rel);
3632 assert_eq!(
3633 entry.to_string(),
3634 "python3-dulwich (>= 0.20.21) | python3-breezy"
3635 );
3636 assert_eq!(
3637 relations.to_string(),
3638 "python3-dulwich (>= 0.20.21) | python3-breezy"
3639 );
3640 }
3641
3642 #[test]
3643 fn test_relations_remove_empty_entry() {
3644 let (mut relations, errors) = Relations::parse_relaxed("foo, , bar, ", false);
3645 assert_eq!(errors, Vec::<String>::new());
3646 assert_eq!(relations.to_string(), "foo, , bar, ");
3647 assert_eq!(relations.len(), 2);
3648 assert_eq!(
3649 relations.entries().next().unwrap().to_string(),
3650 "foo".to_string()
3651 );
3652 assert_eq!(
3653 relations.entries().nth(1).unwrap().to_string(),
3654 "bar".to_string()
3655 );
3656 relations.remove_entry(1);
3657 assert_eq!(relations.to_string(), "foo, , ");
3658 }
3659
3660 #[test]
3661 fn test_entry_remove_relation() {
3662 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
3663 let removed = entry.remove_relation(0);
3664 assert_eq!(removed.to_string(), "python3-dulwich");
3665 assert_eq!(entry.to_string(), "samba");
3666 }
3667
3668 #[test]
3669 fn test_wrap_and_sort_removes_empty_entries() {
3670 let relations: Relations = "foo, , bar, ".parse().unwrap();
3671 let wrapped = relations.wrap_and_sort();
3672 assert_eq!(wrapped.to_string(), "bar, foo");
3673 }
3674
3675 #[test]
3676 fn test_set_archqual() {
3677 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
3678 let mut rel = entry.relations().next().unwrap();
3679 rel.set_archqual("amd64");
3680 assert_eq!(rel.to_string(), "python3-dulwich:amd64");
3681 assert_eq!(rel.archqual(), Some("amd64".to_string()));
3682 assert_eq!(entry.to_string(), "python3-dulwich:amd64 | samba");
3683 rel.set_archqual("i386");
3684 assert_eq!(rel.to_string(), "python3-dulwich:i386");
3685 assert_eq!(rel.archqual(), Some("i386".to_string()));
3686 assert_eq!(entry.to_string(), "python3-dulwich:i386 | samba");
3687 }
3688
3689 #[test]
3690 fn test_set_architectures() {
3691 let mut relation = Relation::simple("samba");
3692 relation.set_architectures(vec!["amd64", "i386"].into_iter());
3693 assert_eq!(relation.to_string(), "samba [amd64 i386]");
3694 }
3695
3696 #[test]
3697 fn test_relation_builder_no_architectures() {
3698 let relation = Relation::build("debhelper").build();
3700 assert_eq!(relation.to_string(), "debhelper");
3701 }
3702
3703 #[test]
3704 fn test_relation_builder_with_architectures() {
3705 let relation = Relation::build("samba")
3707 .architectures(vec!["amd64".to_string(), "i386".to_string()])
3708 .build();
3709 assert_eq!(relation.to_string(), "samba [amd64 i386]");
3710 }
3711
3712 #[test]
3713 fn test_ensure_minimum_version_add_new() {
3714 let mut relations: Relations = "python3".parse().unwrap();
3715 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3716 assert_eq!(relations.to_string(), "python3, debhelper (>= 12)");
3717 }
3718
3719 #[test]
3720 fn test_ensure_minimum_version_update() {
3721 let mut relations: Relations = "debhelper (>= 9)".parse().unwrap();
3722 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3723 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3724 }
3725
3726 #[test]
3727 fn test_ensure_minimum_version_no_change() {
3728 let mut relations: Relations = "debhelper (>= 13)".parse().unwrap();
3729 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3730 assert_eq!(relations.to_string(), "debhelper (>= 13)");
3731 }
3732
3733 #[test]
3734 fn test_ensure_minimum_version_no_version() {
3735 let mut relations: Relations = "debhelper".parse().unwrap();
3736 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3737 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3738 }
3739
3740 #[test]
3741 fn test_ensure_minimum_version_preserves_newline() {
3742 let input = "\n debhelper (>= 9),\n pkg-config,\n uuid-dev";
3748 let mut relations: Relations = input.parse().unwrap();
3749 relations.ensure_minimum_version("debhelper", &"12~".parse().unwrap());
3750 let result = relations.to_string();
3751
3752 assert!(
3754 result.starts_with('\n'),
3755 "Expected result to start with newline, got: {:?}",
3756 result
3757 );
3758 assert_eq!(result, "\n debhelper (>= 12~),\n pkg-config,\n uuid-dev");
3759 }
3760
3761 #[test]
3762 fn test_ensure_minimum_version_preserves_newline_in_control() {
3763 use crate::lossless::Control;
3765 use std::str::FromStr;
3766
3767 let input = r#"Source: f2fs-tools
3768Section: admin
3769Priority: optional
3770Maintainer: Test <test@example.com>
3771Build-Depends:
3772 debhelper (>= 9),
3773 pkg-config,
3774 uuid-dev
3775
3776Package: f2fs-tools
3777Description: test
3778"#;
3779
3780 let control = Control::from_str(input).unwrap();
3781 let mut source = control.source().unwrap();
3782 let mut build_depends = source.build_depends().unwrap();
3783
3784 let version = Version::from_str("12~").unwrap();
3785 build_depends.ensure_minimum_version("debhelper", &version);
3786
3787 source.set_build_depends(&build_depends);
3788
3789 let output = control.to_string();
3790
3791 assert!(
3793 output.contains("Build-Depends:\n debhelper (>= 12~)"),
3794 "Expected 'Build-Depends:\\n debhelper (>= 12~)' but got:\n{}",
3795 output
3796 );
3797 }
3798
3799 #[test]
3800 fn test_ensure_exact_version_add_new() {
3801 let mut relations: Relations = "python3".parse().unwrap();
3802 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3803 assert_eq!(relations.to_string(), "python3, debhelper (= 12)");
3804 }
3805
3806 #[test]
3807 fn test_ensure_exact_version_update() {
3808 let mut relations: Relations = "debhelper (>= 9)".parse().unwrap();
3809 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3810 assert_eq!(relations.to_string(), "debhelper (= 12)");
3811 }
3812
3813 #[test]
3814 fn test_ensure_exact_version_no_change() {
3815 let mut relations: Relations = "debhelper (= 12)".parse().unwrap();
3816 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3817 assert_eq!(relations.to_string(), "debhelper (= 12)");
3818 }
3819
3820 #[test]
3821 fn test_ensure_some_version_add_new() {
3822 let mut relations: Relations = "python3".parse().unwrap();
3823 relations.ensure_some_version("debhelper");
3824 assert_eq!(relations.to_string(), "python3, debhelper");
3825 }
3826
3827 #[test]
3828 fn test_ensure_some_version_exists_with_version() {
3829 let mut relations: Relations = "debhelper (>= 12)".parse().unwrap();
3830 relations.ensure_some_version("debhelper");
3831 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3832 }
3833
3834 #[test]
3835 fn test_ensure_some_version_exists_no_version() {
3836 let mut relations: Relations = "debhelper".parse().unwrap();
3837 relations.ensure_some_version("debhelper");
3838 assert_eq!(relations.to_string(), "debhelper");
3839 }
3840
3841 #[test]
3842 fn test_ensure_substvar() {
3843 let mut relations: Relations = "python3".parse().unwrap();
3844 relations.ensure_substvar("${misc:Depends}").unwrap();
3845 assert_eq!(relations.to_string(), "python3, ${misc:Depends}");
3846 }
3847
3848 #[test]
3849 fn test_ensure_substvar_already_exists() {
3850 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
3851 relations.ensure_substvar("${misc:Depends}").unwrap();
3852 assert_eq!(relations.to_string(), "python3, ${misc:Depends}");
3853 }
3854
3855 #[test]
3856 fn test_ensure_substvar_empty_relations() {
3857 let mut relations: Relations = Relations::new();
3858 relations.ensure_substvar("${misc:Depends}").unwrap();
3859 assert_eq!(relations.to_string(), "${misc:Depends}");
3860 }
3861
3862 #[test]
3863 fn test_ensure_substvar_preserves_whitespace() {
3864 let (mut relations, _) = Relations::parse_relaxed("python3, rustc", false);
3866 relations.ensure_substvar("${misc:Depends}").unwrap();
3867 assert_eq!(relations.to_string(), "python3, rustc, ${misc:Depends}");
3869 }
3870
3871 #[test]
3872 fn test_drop_substvar_basic() {
3873 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
3874 relations.drop_substvar("${misc:Depends}");
3875 assert_eq!(relations.to_string(), "python3");
3876 }
3877
3878 #[test]
3879 fn test_drop_substvar_first_position() {
3880 let (mut relations, _) = Relations::parse_relaxed("${misc:Depends}, python3", true);
3881 relations.drop_substvar("${misc:Depends}");
3882 assert_eq!(relations.to_string(), "python3");
3883 }
3884
3885 #[test]
3886 fn test_drop_substvar_middle_position() {
3887 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}, rustc", true);
3888 relations.drop_substvar("${misc:Depends}");
3889 assert_eq!(relations.to_string(), "python3, rustc");
3890 }
3891
3892 #[test]
3893 fn test_drop_substvar_only_substvar() {
3894 let (mut relations, _) = Relations::parse_relaxed("${misc:Depends}", true);
3895 relations.drop_substvar("${misc:Depends}");
3896 assert_eq!(relations.to_string(), "");
3897 }
3898
3899 #[test]
3900 fn test_drop_substvar_not_exists() {
3901 let (mut relations, _) = Relations::parse_relaxed("python3, rustc", false);
3902 relations.drop_substvar("${misc:Depends}");
3903 assert_eq!(relations.to_string(), "python3, rustc");
3904 }
3905
3906 #[test]
3907 fn test_drop_substvar_multiple_substvars() {
3908 let (mut relations, _) =
3909 Relations::parse_relaxed("python3, ${misc:Depends}, ${shlibs:Depends}", true);
3910 relations.drop_substvar("${misc:Depends}");
3911 assert_eq!(relations.to_string(), "python3, ${shlibs:Depends}");
3912 }
3913
3914 #[test]
3915 fn test_drop_substvar_preserves_whitespace() {
3916 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
3917 relations.drop_substvar("${misc:Depends}");
3918 assert_eq!(relations.to_string(), "python3");
3919 }
3920
3921 #[test]
3922 fn test_filter_entries_basic() {
3923 let mut relations: Relations = "python3, debhelper, rustc".parse().unwrap();
3924 relations.filter_entries(|entry| entry.relations().any(|r| r.name().starts_with("python")));
3925 assert_eq!(relations.to_string(), "python3");
3926 }
3927
3928 #[test]
3929 fn test_filter_entries_keep_all() {
3930 let mut relations: Relations = "python3, debhelper".parse().unwrap();
3931 relations.filter_entries(|_| true);
3932 assert_eq!(relations.to_string(), "python3, debhelper");
3933 }
3934
3935 #[test]
3936 fn test_filter_entries_remove_all() {
3937 let mut relations: Relations = "python3, debhelper".parse().unwrap();
3938 relations.filter_entries(|_| false);
3939 assert_eq!(relations.to_string(), "");
3940 }
3941
3942 #[test]
3943 fn test_filter_entries_keep_middle() {
3944 let mut relations: Relations = "aaa, bbb, ccc".parse().unwrap();
3945 relations.filter_entries(|entry| entry.relations().any(|r| r.name() == "bbb"));
3946 assert_eq!(relations.to_string(), "bbb");
3947 }
3948
3949 #[test]
3952 fn test_is_sorted_wrap_and_sort_order() {
3953 let relations: Relations = "debhelper, python3, rustc".parse().unwrap();
3955 assert!(relations.is_sorted(&WrapAndSortOrder));
3956
3957 let relations: Relations = "rustc, debhelper, python3".parse().unwrap();
3959 assert!(!relations.is_sorted(&WrapAndSortOrder));
3960
3961 let (relations, _) =
3963 Relations::parse_relaxed("cdbs, debhelper-compat, python3, ${misc:Depends}", true);
3964 assert!(relations.is_sorted(&WrapAndSortOrder));
3965 }
3966
3967 #[test]
3968 fn test_is_sorted_default_order() {
3969 let relations: Relations = "aaa, bbb, ccc".parse().unwrap();
3971 assert!(relations.is_sorted(&DefaultSortingOrder));
3972
3973 let relations: Relations = "ccc, aaa, bbb".parse().unwrap();
3975 assert!(!relations.is_sorted(&DefaultSortingOrder));
3976
3977 let (relations, _) = Relations::parse_relaxed("aaa, bbb, ${misc:Depends}", true);
3979 assert!(relations.is_sorted(&DefaultSortingOrder));
3980 }
3981
3982 #[test]
3983 fn test_is_sorted_with_substvars() {
3984 let (relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}, rustc", true);
3986 assert!(relations.is_sorted(&DefaultSortingOrder));
3988 }
3989
3990 #[test]
3991 fn test_drop_dependency_exists() {
3992 let mut relations: Relations = "python3, debhelper, rustc".parse().unwrap();
3993 assert!(relations.drop_dependency("debhelper"));
3994 assert_eq!(relations.to_string(), "python3, rustc");
3995 }
3996
3997 #[test]
3998 fn test_drop_dependency_not_exists() {
3999 let mut relations: Relations = "python3, rustc".parse().unwrap();
4000 assert!(!relations.drop_dependency("nonexistent"));
4001 assert_eq!(relations.to_string(), "python3, rustc");
4002 }
4003
4004 #[test]
4005 fn test_drop_dependency_only_item() {
4006 let mut relations: Relations = "python3".parse().unwrap();
4007 assert!(relations.drop_dependency("python3"));
4008 assert_eq!(relations.to_string(), "");
4009 }
4010
4011 #[test]
4012 fn test_add_dependency_to_empty() {
4013 let mut relations: Relations = "".parse().unwrap();
4014 let entry = Entry::from(Relation::simple("python3"));
4015 relations.add_dependency(entry, None);
4016 assert_eq!(relations.to_string(), "python3");
4017 }
4018
4019 #[test]
4020 fn test_add_dependency_sorted_position() {
4021 let mut relations: Relations = "debhelper, rustc".parse().unwrap();
4022 let entry = Entry::from(Relation::simple("python3"));
4023 relations.add_dependency(entry, None);
4024 assert_eq!(relations.to_string(), "debhelper, python3, rustc");
4026 }
4027
4028 #[test]
4029 fn test_add_dependency_explicit_position() {
4030 let mut relations: Relations = "python3, rustc".parse().unwrap();
4031 let entry = Entry::from(Relation::simple("debhelper"));
4032 relations.add_dependency(entry, Some(0));
4033 assert_eq!(relations.to_string(), "debhelper, python3, rustc");
4034 }
4035
4036 #[test]
4037 fn test_add_dependency_build_system_first() {
4038 let mut relations: Relations = "python3, rustc".parse().unwrap();
4039 let entry = Entry::from(Relation::simple("debhelper-compat"));
4040 relations.add_dependency(entry, None);
4041 assert_eq!(relations.to_string(), "debhelper-compat, python3, rustc");
4043 }
4044
4045 #[test]
4046 fn test_add_dependency_at_end() {
4047 let mut relations: Relations = "debhelper, python3".parse().unwrap();
4048 let entry = Entry::from(Relation::simple("zzz-package"));
4049 relations.add_dependency(entry, None);
4050 assert_eq!(relations.to_string(), "debhelper, python3, zzz-package");
4052 }
4053
4054 #[test]
4055 fn test_add_dependency_to_single_entry() {
4056 let mut relations: Relations = "python3-dulwich".parse().unwrap();
4058 let entry: Entry = "debhelper-compat (= 12)".parse().unwrap();
4059 relations.add_dependency(entry, None);
4060 assert_eq!(
4062 relations.to_string(),
4063 "debhelper-compat (= 12), python3-dulwich"
4064 );
4065 }
4066
4067 #[test]
4068 fn test_get_relation_exists() {
4069 let relations: Relations = "python3, debhelper (>= 12), rustc".parse().unwrap();
4070 let result = relations.get_relation("debhelper");
4071 assert!(result.is_ok());
4072 let (idx, entry) = result.unwrap();
4073 assert_eq!(idx, 1);
4074 assert_eq!(entry.to_string(), "debhelper (>= 12)");
4075 }
4076
4077 #[test]
4078 fn test_get_relation_not_exists() {
4079 let relations: Relations = "python3, rustc".parse().unwrap();
4080 let result = relations.get_relation("nonexistent");
4081 assert_eq!(result, Err("Package nonexistent not found".to_string()));
4082 }
4083
4084 #[test]
4085 fn test_get_relation_complex_rule() {
4086 let relations: Relations = "python3 | python3-minimal, rustc".parse().unwrap();
4087 let result = relations.get_relation("python3");
4088 assert_eq!(
4089 result,
4090 Err("Complex rule for python3, aborting".to_string())
4091 );
4092 }
4093
4094 #[test]
4095 fn test_iter_relations_for_simple() {
4096 let relations: Relations = "python3, debhelper, python3-dev".parse().unwrap();
4097 let entries: Vec<_> = relations.iter_relations_for("python3").collect();
4098 assert_eq!(entries.len(), 1);
4099 assert_eq!(entries[0].0, 0);
4100 assert_eq!(entries[0].1.to_string(), "python3");
4101 }
4102
4103 #[test]
4104 fn test_iter_relations_for_alternatives() {
4105 let relations: Relations = "python3 | python3-minimal, python3-dev".parse().unwrap();
4106 let entries: Vec<_> = relations.iter_relations_for("python3").collect();
4107 assert_eq!(entries.len(), 1);
4109 assert_eq!(entries[0].0, 0);
4110 }
4111
4112 #[test]
4113 fn test_iter_relations_for_not_found() {
4114 let relations: Relations = "python3, rustc".parse().unwrap();
4115 let entries: Vec<_> = relations.iter_relations_for("debhelper").collect();
4116 assert_eq!(entries.len(), 0);
4117 }
4118
4119 #[test]
4120 fn test_has_relation_exists() {
4121 let relations: Relations = "python3, debhelper, rustc".parse().unwrap();
4122 assert!(relations.has_relation("debhelper"));
4123 assert!(relations.has_relation("python3"));
4124 assert!(relations.has_relation("rustc"));
4125 }
4126
4127 #[test]
4128 fn test_has_relation_not_exists() {
4129 let relations: Relations = "python3, rustc".parse().unwrap();
4130 assert!(!relations.has_relation("debhelper"));
4131 }
4132
4133 #[test]
4134 fn test_has_relation_in_alternative() {
4135 let relations: Relations = "python3 | python3-minimal".parse().unwrap();
4136 assert!(relations.has_relation("python3"));
4137 assert!(relations.has_relation("python3-minimal"));
4138 }
4139
4140 #[test]
4141 fn test_sorting_order_wrap_and_sort_build_systems() {
4142 let order = WrapAndSortOrder;
4143 assert!(order.lt("debhelper", "python3"));
4145 assert!(order.lt("debhelper-compat", "rustc"));
4146 assert!(order.lt("cdbs", "aaa"));
4147 assert!(order.lt("dh-python", "python3"));
4148 }
4149
4150 #[test]
4151 fn test_sorting_order_wrap_and_sort_regular_packages() {
4152 let order = WrapAndSortOrder;
4153 assert!(order.lt("aaa", "bbb"));
4155 assert!(order.lt("python3", "rustc"));
4156 assert!(!order.lt("rustc", "python3"));
4157 }
4158
4159 #[test]
4160 fn test_sorting_order_wrap_and_sort_substvars() {
4161 let order = WrapAndSortOrder;
4162 assert!(order.lt("python3", "${misc:Depends}"));
4164 assert!(!order.lt("${misc:Depends}", "python3"));
4165 assert!(!order.ignore("${misc:Depends}"));
4167 }
4168
4169 #[test]
4170 fn test_sorting_order_default_special_items() {
4171 let order = DefaultSortingOrder;
4172 assert!(order.lt("python3", "${misc:Depends}"));
4174 assert!(order.lt("aaa", "@cdbs@"));
4175 assert!(order.ignore("${misc:Depends}"));
4177 assert!(order.ignore("@cdbs@"));
4178 assert!(!order.ignore("python3"));
4179 }
4180
4181 #[test]
4182 fn test_is_special_package_name() {
4183 assert!(is_special_package_name("${misc:Depends}"));
4184 assert!(is_special_package_name("${shlibs:Depends}"));
4185 assert!(is_special_package_name("@cdbs@"));
4186 assert!(!is_special_package_name("python3"));
4187 assert!(!is_special_package_name("debhelper"));
4188 }
4189
4190 #[test]
4191 fn test_add_dependency_with_explicit_position() {
4192 let mut relations: Relations = "python3, rustc".parse().unwrap();
4194 let entry = Entry::from(Relation::simple("debhelper"));
4195 relations.add_dependency(entry, Some(1));
4196 assert_eq!(relations.to_string(), "python3, debhelper, rustc");
4198 }
4199
4200 #[test]
4201 fn test_whitespace_detection_single_space() {
4202 let mut relations: Relations = "python3, rustc".parse().unwrap();
4203 let entry = Entry::from(Relation::simple("debhelper"));
4204 relations.add_dependency(entry, Some(1));
4205 assert_eq!(relations.to_string(), "python3, debhelper, rustc");
4206 }
4207
4208 #[test]
4209 fn test_whitespace_detection_multiple_spaces() {
4210 let mut relations: Relations = "python3, rustc, gcc".parse().unwrap();
4211 let entry = Entry::from(Relation::simple("debhelper"));
4212 relations.add_dependency(entry, Some(1));
4213 assert_eq!(relations.to_string(), "python3, debhelper, rustc, gcc");
4215 }
4216
4217 #[test]
4218 fn test_whitespace_detection_mixed_patterns() {
4219 let mut relations: Relations = "a, b, c, d, e".parse().unwrap();
4221 let entry = Entry::from(Relation::simple("x"));
4222 relations.push(entry);
4223 assert_eq!(relations.to_string(), "a, b, c, d, e, x");
4226 }
4227
4228 #[test]
4229 fn test_whitespace_detection_newlines() {
4230 let mut relations: Relations = "python3,\n 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,\n debhelper,\n rustc");
4235 }
4236
4237 #[test]
4238 fn test_append_with_newline_no_trailing() {
4239 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4240 let entry = Entry::from(Relation::simple("blah"));
4241 relations.add_dependency(entry, None);
4242 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4243 }
4244
4245 #[test]
4246 fn test_append_with_trailing_newline() {
4247 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4248 let entry = Entry::from(Relation::simple("blah"));
4249 relations.add_dependency(entry, None);
4250 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4251 }
4252
4253 #[test]
4254 fn test_append_with_4_space_indent() {
4255 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4256 let entry = Entry::from(Relation::simple("blah"));
4257 relations.add_dependency(entry, None);
4258 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4259 }
4260
4261 #[test]
4262 fn test_append_with_4_space_and_trailing_newline() {
4263 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4264 let entry = Entry::from(Relation::simple("blah"));
4265 relations.add_dependency(entry, None);
4266 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4267 }
4268
4269 #[test]
4270 fn test_odd_syntax_append_no_trailing() {
4271 let mut relations: Relations = "\n foo\n , bar".parse().unwrap();
4272 let entry = Entry::from(Relation::simple("blah"));
4273 relations.add_dependency(entry, None);
4274 assert_eq!(relations.to_string(), "\n foo\n , bar\n , blah");
4275 }
4276
4277 #[test]
4278 fn test_odd_syntax_append_with_trailing() {
4279 let mut relations: Relations = "\n foo\n , bar\n".parse().unwrap();
4280 let entry = Entry::from(Relation::simple("blah"));
4281 relations.add_dependency(entry, None);
4282 assert_eq!(relations.to_string(), "\n foo\n , bar\n , blah");
4283 }
4284
4285 #[test]
4286 fn test_insert_at_1_no_trailing() {
4287 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4288 let entry = Entry::from(Relation::simple("blah"));
4289 relations.add_dependency(entry, Some(1));
4290 assert_eq!(relations.to_string(), "foo,\n blah,\n bar");
4291 }
4292
4293 #[test]
4294 fn test_insert_at_1_with_trailing() {
4295 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4296 let entry = Entry::from(Relation::simple("blah"));
4297 relations.add_dependency(entry, Some(1));
4298 assert_eq!(relations.to_string(), "foo,\n blah,\n bar");
4299 }
4300
4301 #[test]
4302 fn test_odd_syntax_insert_at_1() {
4303 let mut relations: Relations = "\n foo\n , bar\n".parse().unwrap();
4304 let entry = Entry::from(Relation::simple("blah"));
4305 relations.add_dependency(entry, Some(1));
4306 assert_eq!(relations.to_string(), "\n foo\n , blah\n , bar");
4307 }
4308
4309 #[test]
4310 fn test_relations_preserves_exact_whitespace() {
4311 let input =
4313 "debhelper (>= 10), quilt (>= 0.40),\n libsystemd-dev [linux-any], pkg-config";
4314
4315 let relations: Relations = input.parse().unwrap();
4316
4317 assert_eq!(
4319 relations.to_string(),
4320 input,
4321 "Relations should preserve exact whitespace from input"
4322 );
4323 }
4324
4325 #[test]
4326 fn test_remove_entry_preserves_indentation() {
4327 let input = "debhelper (>= 10), quilt (>= 0.40),\n libsystemd-dev [linux-any], dh-systemd (>= 1.5), pkg-config";
4329
4330 let mut relations: Relations = input.parse().unwrap();
4331
4332 let mut to_remove = Vec::new();
4334 for (idx, entry) in relations.entries().enumerate() {
4335 for relation in entry.relations() {
4336 if relation.name() == "dh-systemd" {
4337 to_remove.push(idx);
4338 break;
4339 }
4340 }
4341 }
4342
4343 for idx in to_remove.into_iter().rev() {
4344 relations.remove_entry(idx);
4345 }
4346
4347 let output = relations.to_string();
4348 println!("After removal: '{}'", output);
4349
4350 assert!(
4352 output.contains("\n libsystemd-dev"),
4353 "Expected 4-space indentation to be preserved, but got:\n'{}'",
4354 output
4355 );
4356 }
4357
4358 #[test]
4359 fn test_relation_is_implied_by_same_package() {
4360 let inner = Relation::new(
4362 "pkg",
4363 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4364 );
4365 let outer = Relation::new(
4366 "pkg",
4367 Some((VersionConstraint::GreaterThanEqual, "1.5".parse().unwrap())),
4368 );
4369 assert!(inner.is_implied_by(&outer));
4370 }
4371
4372 #[test]
4373 fn test_relation_is_implied_by_different_package() {
4374 let inner = Relation::new("pkg1", None);
4376 let outer = Relation::new("pkg2", None);
4377 assert!(!inner.is_implied_by(&outer));
4378 }
4379
4380 #[test]
4381 fn test_relation_is_implied_by_no_version() {
4382 let inner = Relation::new("pkg", None);
4384 let outer = Relation::new(
4385 "pkg",
4386 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4387 );
4388 assert!(inner.is_implied_by(&outer));
4389 }
4390
4391 #[test]
4392 fn test_relation_is_implied_by_identical() {
4393 let inner = Relation::new(
4395 "pkg",
4396 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4397 );
4398 let outer = Relation::new(
4399 "pkg",
4400 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4401 );
4402 assert!(inner.is_implied_by(&outer));
4403 assert!(outer.is_implied_by(&inner));
4404 }
4405
4406 #[test]
4407 fn test_relation_is_implied_by_greater_than_equal() {
4408 let inner = Relation::new(
4410 "pkg",
4411 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4412 );
4413 let outer = Relation::new(
4414 "pkg",
4415 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
4416 );
4417 assert!(inner.is_implied_by(&outer));
4418 assert!(!outer.is_implied_by(&inner));
4419
4420 let outer = Relation::new(
4422 "pkg",
4423 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4424 );
4425 assert!(inner.is_implied_by(&outer));
4426
4427 let outer = Relation::new(
4429 "pkg",
4430 Some((VersionConstraint::GreaterThan, "1.5".parse().unwrap())),
4431 );
4432 assert!(inner.is_implied_by(&outer));
4433
4434 let inner = Relation::new(
4436 "pkg",
4437 Some((VersionConstraint::GreaterThanEqual, "3.0".parse().unwrap())),
4438 );
4439 let outer = Relation::new(
4440 "pkg",
4441 Some((VersionConstraint::GreaterThan, "3.0".parse().unwrap())),
4442 );
4443 assert!(!inner.is_implied_by(&outer));
4444 }
4445
4446 #[test]
4447 fn test_relation_is_implied_by_less_than_equal() {
4448 let inner = Relation::new(
4450 "pkg",
4451 Some((VersionConstraint::LessThanEqual, "2.0".parse().unwrap())),
4452 );
4453 let outer = Relation::new(
4454 "pkg",
4455 Some((VersionConstraint::LessThanEqual, "1.0".parse().unwrap())),
4456 );
4457 assert!(inner.is_implied_by(&outer));
4458 assert!(!outer.is_implied_by(&inner));
4459
4460 let outer = Relation::new(
4462 "pkg",
4463 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4464 );
4465 assert!(inner.is_implied_by(&outer));
4466
4467 let outer = Relation::new(
4469 "pkg",
4470 Some((VersionConstraint::LessThan, "1.5".parse().unwrap())),
4471 );
4472 assert!(inner.is_implied_by(&outer));
4473 }
4474
4475 #[test]
4476 fn test_relation_is_implied_by_equal() {
4477 let inner = Relation::new(
4479 "pkg",
4480 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4481 );
4482 let outer = Relation::new(
4483 "pkg",
4484 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4485 );
4486 assert!(inner.is_implied_by(&outer));
4487
4488 let outer = Relation::new(
4490 "pkg",
4491 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4492 );
4493 assert!(!inner.is_implied_by(&outer));
4494
4495 let outer = Relation::new(
4497 "pkg",
4498 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4499 );
4500 assert!(!inner.is_implied_by(&outer));
4501 }
4502
4503 #[test]
4504 fn test_relation_is_implied_by_greater_than() {
4505 let inner = Relation::new(
4507 "pkg",
4508 Some((VersionConstraint::GreaterThan, "1.0".parse().unwrap())),
4509 );
4510 let outer = Relation::new(
4511 "pkg",
4512 Some((VersionConstraint::GreaterThan, "2.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.5".parse().unwrap())),
4527 );
4528 assert!(inner.is_implied_by(&outer));
4529
4530 let outer = Relation::new(
4532 "pkg",
4533 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4534 );
4535 assert!(!inner.is_implied_by(&outer));
4536 }
4537
4538 #[test]
4539 fn test_relation_is_implied_by_less_than() {
4540 let inner = Relation::new(
4542 "pkg",
4543 Some((VersionConstraint::LessThan, "2.0".parse().unwrap())),
4544 );
4545 let outer = Relation::new(
4546 "pkg",
4547 Some((VersionConstraint::LessThan, "1.0".parse().unwrap())),
4548 );
4549 assert!(inner.is_implied_by(&outer));
4550
4551 let outer = Relation::new(
4553 "pkg",
4554 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4555 );
4556 assert!(inner.is_implied_by(&outer));
4557
4558 let outer = Relation::new(
4560 "pkg",
4561 Some((VersionConstraint::LessThanEqual, "1.5".parse().unwrap())),
4562 );
4563 assert!(inner.is_implied_by(&outer));
4564 }
4565
4566 #[test]
4567 fn test_relation_is_implied_by_incompatible_constraints() {
4568 let inner = Relation::new(
4570 "pkg",
4571 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4572 );
4573 let outer = Relation::new(
4574 "pkg",
4575 Some((VersionConstraint::LessThanEqual, "2.0".parse().unwrap())),
4576 );
4577 assert!(!inner.is_implied_by(&outer));
4578 assert!(!outer.is_implied_by(&inner));
4579 }
4580
4581 #[test]
4582 fn test_entry_is_implied_by_identical() {
4583 let inner: Entry = "pkg (>= 1.0)".parse().unwrap();
4584 let outer: Entry = "pkg (>= 1.0)".parse().unwrap();
4585 assert!(inner.is_implied_by(&outer));
4586 }
4587
4588 #[test]
4589 fn test_entry_is_implied_by_or_group() {
4590 let inner: Entry = "pkg (>= 1.0)".parse().unwrap();
4592 let outer: Entry = "pkg (>= 1.5) | libc6".parse().unwrap();
4593 assert!(inner.is_implied_by(&outer));
4594 }
4595
4596 #[test]
4597 fn test_entry_is_implied_by_simple_or() {
4598 let inner: Entry = "pkg1 | pkg2".parse().unwrap();
4600 let outer: Entry = "pkg1".parse().unwrap();
4601 assert!(inner.is_implied_by(&outer));
4602
4603 let outer: Entry = "pkg2".parse().unwrap();
4605 assert!(inner.is_implied_by(&outer));
4606 }
4607
4608 #[test]
4609 fn test_entry_is_implied_by_not_implied() {
4610 let inner: Entry = "pkg (>= 2.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_different_packages() {
4618 let inner: Entry = "pkg1".parse().unwrap();
4619 let outer: Entry = "pkg2".parse().unwrap();
4620 assert!(!inner.is_implied_by(&outer));
4621 }
4622
4623 #[test]
4624 fn test_entry_is_implied_by_complex_or() {
4625 let inner: Entry = "pkg1 | pkg2".parse().unwrap();
4627 let outer: Entry = "pkg1 | pkg2".parse().unwrap();
4628 assert!(inner.is_implied_by(&outer));
4629
4630 let outer: Entry = "pkg1 | pkg2 | pkg3".parse().unwrap();
4632 assert!(inner.is_implied_by(&outer));
4633 }
4634}