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)]
46pub enum 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 break;
108 }
109 }
110 }
111 if self.current() != Some(R_CURLY) {
112 self.error(format!("expected }} but got {:?}", self.current()).to_string());
113 } else {
114 self.bump();
115 }
116 self.builder.finish_node();
117 }
118
119 fn parse_entry(&mut self) {
120 self.skip_ws();
121 self.builder.start_node(SyntaxKind::ENTRY.into());
122 loop {
123 self.parse_relation();
124 match self.peek_past_ws() {
125 Some(COMMA) => {
126 break;
127 }
128 Some(PIPE) => {
129 self.skip_ws();
130 self.bump();
131 self.skip_ws();
132 }
133 None => {
134 self.skip_ws();
135 break;
136 }
137 _ => {
138 self.skip_ws();
139 self.builder.start_node(SyntaxKind::ERROR.into());
140 match self.tokens.pop() {
141 Some((k, t)) => {
142 self.builder.token(k.into(), t.as_str());
143 self.errors
144 .push(format!("Expected comma or pipe, not {:?}", (k, t)));
145 }
146 None => {
147 self.errors
148 .push("Expected comma or pipe, got end of file".to_string());
149 }
150 }
151 self.builder.finish_node();
152 }
153 }
154 }
155 self.builder.finish_node();
156 }
157
158 fn error(&mut self, error: String) {
159 self.errors.push(error);
160 self.builder.start_node(SyntaxKind::ERROR.into());
161 if self.current().is_some() {
162 self.bump();
163 }
164 self.builder.finish_node();
165 }
166
167 fn parse_relation(&mut self) {
168 self.builder.start_node(SyntaxKind::RELATION.into());
169 if self.current() == Some(IDENT) {
170 self.bump();
171 } else {
172 self.error("Expected package name".to_string());
173 }
174 match self.peek_past_ws() {
175 Some(COLON) => {
176 self.skip_ws();
177 self.builder.start_node(ARCHQUAL.into());
178 self.bump();
179 self.skip_ws();
180 if self.current() == Some(IDENT) {
181 self.bump();
182 } else {
183 self.error("Expected architecture name".to_string());
184 }
185 self.builder.finish_node();
186 self.skip_ws();
187 }
188 Some(PIPE) | Some(COMMA) => {}
189 None | Some(L_PARENS) | Some(L_BRACKET) | Some(L_ANGLE) => {
190 self.skip_ws();
191 }
192 e => {
193 self.skip_ws();
194 self.error(format!(
195 "Expected ':' or '|' or '[' or '<' or ',' but got {:?}",
196 e
197 ));
198 }
199 }
200
201 if self.peek_past_ws() == Some(L_PARENS) {
202 self.skip_ws();
203 self.builder.start_node(VERSION.into());
204 self.bump();
205 self.skip_ws();
206
207 self.builder.start_node(CONSTRAINT.into());
208
209 while self.current() == Some(L_ANGLE)
210 || self.current() == Some(R_ANGLE)
211 || self.current() == Some(EQUAL)
212 {
213 self.bump();
214 }
215
216 self.builder.finish_node();
217
218 self.skip_ws();
219
220 while matches!(self.current(), Some(IDENT) | Some(COLON)) {
223 self.bump();
224 }
225
226 if self.current() == Some(R_PARENS) {
227 self.bump();
228 } else {
229 self.error("Expected ')'".to_string());
230 }
231
232 self.builder.finish_node();
233 }
234
235 if self.peek_past_ws() == Some(L_BRACKET) {
236 self.skip_ws();
237 self.builder.start_node(ARCHITECTURES.into());
238 self.bump();
239 loop {
240 self.skip_ws();
241 match self.current() {
242 Some(NOT) => {
243 self.bump();
244 }
245 Some(IDENT) => {
246 self.bump();
247 }
248 Some(R_BRACKET) => {
249 self.bump();
250 break;
251 }
252 _ => {
253 self.error("Expected architecture name or '!' or ']'".to_string());
254 break;
255 }
256 }
257 }
258 self.builder.finish_node();
259 }
260
261 while self.peek_past_ws() == Some(L_ANGLE) {
262 self.skip_ws();
263 self.builder.start_node(PROFILES.into());
264 self.bump();
265
266 loop {
267 self.skip_ws();
268 match self.current() {
269 Some(IDENT) => {
270 self.bump();
271 }
272 Some(NOT) => {
273 self.bump();
274 self.skip_ws();
275 if self.current() == Some(IDENT) {
276 self.bump();
277 } else {
278 self.error("Expected profile".to_string());
279 }
280 }
281 Some(R_ANGLE) => {
282 self.bump();
283 break;
284 }
285 None => {
286 self.error("Expected profile or '>'".to_string());
287 break;
288 }
289 _ => {
290 self.error("Expected profile or '!' or '>'".to_string());
291 break;
292 }
293 }
294 }
295
296 self.builder.finish_node();
297 }
298
299 self.builder.finish_node();
300 }
301
302 fn parse(mut self) -> Parse {
303 self.builder.start_node(SyntaxKind::ROOT.into());
304
305 self.skip_ws();
306
307 while self.current().is_some() {
308 match self.current() {
309 Some(IDENT) => self.parse_entry(),
310 Some(DOLLAR) => {
311 if self.allow_substvar {
312 self.parse_substvar()
313 } else {
314 self.error("Substvars are not allowed".to_string());
315 }
316 }
317 Some(COMMA) => {
318 }
320 Some(c) => {
321 self.error(format!("expected $ or identifier but got {:?}", c));
322 }
323 None => {
324 self.error("expected identifier but got end of file".to_string());
325 }
326 }
327
328 self.skip_ws();
329 match self.current() {
330 Some(COMMA) => {
331 self.bump();
332 }
333 None => {
334 break;
335 }
336 c => {
337 self.error(format!("expected comma or end of file but got {:?}", c));
338 }
339 }
340 self.skip_ws();
341 }
342
343 self.builder.finish_node();
344 Parse {
346 green_node: self.builder.finish(),
347 errors: self.errors,
348 }
349 }
350 fn bump(&mut self) {
352 let (kind, text) = self.tokens.pop().unwrap();
353 self.builder.token(kind.into(), text.as_str());
354 }
355 fn current(&self) -> Option<SyntaxKind> {
357 self.tokens.last().map(|(kind, _)| *kind)
358 }
359 fn skip_ws(&mut self) {
360 while self.current() == Some(WHITESPACE) || self.current() == Some(NEWLINE) {
361 self.bump()
362 }
363 }
364
365 fn peek_past_ws(&self) -> Option<SyntaxKind> {
366 let mut i = self.tokens.len();
367 while i > 0 {
368 i -= 1;
369 match self.tokens[i].0 {
370 WHITESPACE | NEWLINE => {}
371 _ => return Some(self.tokens[i].0),
372 }
373 }
374 None
375 }
376 }
377
378 let mut tokens = crate::relations::lex(text);
379 tokens.reverse();
380 Parser {
381 tokens,
382 builder: GreenNodeBuilder::new(),
383 errors: Vec::new(),
384 allow_substvar,
385 }
386 .parse()
387}
388
389pub type SyntaxNode = rowan::SyntaxNode<Lang>;
397pub type SyntaxToken = rowan::SyntaxToken<Lang>;
399pub type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;
401
402impl Parse {
403 fn root_mut(&self) -> Relations {
404 Relations::cast(SyntaxNode::new_root_mut(self.green_node.clone())).unwrap()
405 }
406}
407
408macro_rules! ast_node {
409 ($ast:ident, $kind:ident) => {
410 #[repr(transparent)]
412 pub struct $ast(SyntaxNode);
413 impl $ast {
414 #[allow(unused)]
415 fn cast(node: SyntaxNode) -> Option<Self> {
416 if node.kind() == $kind {
417 Some(Self(node))
418 } else {
419 None
420 }
421 }
422
423 pub fn syntax(&self) -> &rowan::SyntaxNode<Lang> {
425 &self.0
426 }
427 }
428
429 impl std::fmt::Display for $ast {
430 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
431 f.write_str(&self.0.text().to_string())
432 }
433 }
434 };
435}
436
437ast_node!(Relations, ROOT);
438ast_node!(Entry, ENTRY);
439ast_node!(Relation, RELATION);
440ast_node!(Substvar, SUBSTVAR);
441
442impl PartialEq for Relations {
443 fn eq(&self, other: &Self) -> bool {
444 self.entries().collect::<Vec<_>>() == other.entries().collect::<Vec<_>>()
445 }
446}
447
448impl PartialEq for Entry {
449 fn eq(&self, other: &Self) -> bool {
450 self.relations().collect::<Vec<_>>() == other.relations().collect::<Vec<_>>()
451 }
452}
453
454impl PartialEq for Relation {
455 fn eq(&self, other: &Self) -> bool {
456 self.name() == other.name()
457 && self.version() == other.version()
458 && self.archqual() == other.archqual()
459 && self.architectures().map(|x| x.collect::<HashSet<_>>())
460 == other.architectures().map(|x| x.collect::<HashSet<_>>())
461 && self.profiles().eq(other.profiles())
462 }
463}
464
465#[cfg(feature = "serde")]
466impl serde::Serialize for Relations {
467 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
468 let rep = self.to_string();
469 serializer.serialize_str(&rep)
470 }
471}
472
473#[cfg(feature = "serde")]
474impl<'de> serde::Deserialize<'de> for Relations {
475 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
476 let s = String::deserialize(deserializer)?;
477 let relations = s.parse().map_err(serde::de::Error::custom)?;
478 Ok(relations)
479 }
480}
481
482impl std::fmt::Debug for Relations {
483 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
484 let mut s = f.debug_struct("Relations");
485
486 for entry in self.entries() {
487 s.field("entry", &entry);
488 }
489
490 s.finish()
491 }
492}
493
494impl std::fmt::Debug for Entry {
495 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
496 let mut s = f.debug_struct("Entry");
497
498 for relation in self.relations() {
499 s.field("relation", &relation);
500 }
501
502 s.finish()
503 }
504}
505
506#[cfg(feature = "serde")]
507impl serde::Serialize for Entry {
508 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
509 let rep = self.to_string();
510 serializer.serialize_str(&rep)
511 }
512}
513
514#[cfg(feature = "serde")]
515impl<'de> serde::Deserialize<'de> for Entry {
516 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
517 let s = String::deserialize(deserializer)?;
518 let entry = s.parse().map_err(serde::de::Error::custom)?;
519 Ok(entry)
520 }
521}
522
523impl std::fmt::Debug for Relation {
524 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
525 let mut s = f.debug_struct("Relation");
526
527 s.field("name", &self.name());
528
529 if let Some((vc, version)) = self.version() {
530 s.field("version", &vc);
531 s.field("version", &version);
532 }
533
534 s.finish()
535 }
536}
537
538#[cfg(feature = "serde")]
539impl serde::Serialize for Relation {
540 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
541 let rep = self.to_string();
542 serializer.serialize_str(&rep)
543 }
544}
545
546#[cfg(feature = "serde")]
547impl<'de> serde::Deserialize<'de> for Relation {
548 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
549 let s = String::deserialize(deserializer)?;
550 let relation = s.parse().map_err(serde::de::Error::custom)?;
551 Ok(relation)
552 }
553}
554
555impl Default for Relations {
556 fn default() -> Self {
557 Self::new()
558 }
559}
560
561fn is_special_package_name(name: &str) -> bool {
566 if name.starts_with("${") && name.ends_with('}') {
568 return true;
569 }
570 if name.starts_with('@') && name.ends_with('@') {
572 return true;
573 }
574 false
575}
576
577pub trait SortingOrder {
579 fn lt(&self, name1: &str, name2: &str) -> bool;
583
584 fn ignore(&self, name: &str) -> bool;
586}
587
588#[derive(Debug, Clone, Copy, Default)]
590pub struct DefaultSortingOrder;
591
592impl SortingOrder for DefaultSortingOrder {
593 fn lt(&self, name1: &str, name2: &str) -> bool {
594 let special1 = is_special_package_name(name1);
595 let special2 = is_special_package_name(name2);
596
597 if special1 && !special2 {
599 return false;
600 }
601 if !special1 && special2 {
602 return true;
603 }
604 if special1 && special2 {
605 return false;
607 }
608
609 name1 < name2
611 }
612
613 fn ignore(&self, name: &str) -> bool {
614 is_special_package_name(name)
615 }
616}
617
618#[derive(Debug, Clone, Copy, Default)]
628pub struct WrapAndSortOrder;
629
630impl WrapAndSortOrder {
631 const BUILD_SYSTEMS: &'static [&'static str] = &[
633 "cdbs",
634 "debhelper-compat",
635 "debhelper",
636 "debputy",
637 "dpkg-build-api",
638 "dpkg-dev",
639 ];
640
641 fn get_sort_key<'a>(&self, name: &'a str) -> (i32, &'a str) {
642 if Self::BUILD_SYSTEMS.contains(&name) || name.starts_with("dh-") {
644 return (-1, name);
645 }
646
647 if name
649 .chars()
650 .next()
651 .is_some_and(|c| c.is_ascii_lowercase() || c.is_ascii_digit())
652 {
653 return (0, name);
654 }
655
656 (1, name)
658 }
659}
660
661impl SortingOrder for WrapAndSortOrder {
662 fn lt(&self, name1: &str, name2: &str) -> bool {
663 self.get_sort_key(name1) < self.get_sort_key(name2)
664 }
665
666 fn ignore(&self, _name: &str) -> bool {
667 false
669 }
670}
671
672impl Relations {
673 pub fn new() -> Self {
675 Self::from(vec![])
676 }
677
678 #[must_use]
680 pub fn wrap_and_sort(self) -> Self {
681 let mut entries = self
682 .entries()
683 .map(|e| e.wrap_and_sort())
684 .collect::<Vec<_>>();
685 entries.sort();
686 Self::from(entries)
688 }
689
690 pub fn entries(&self) -> impl Iterator<Item = Entry> + '_ {
692 self.0.children().filter_map(Entry::cast)
693 }
694
695 pub fn iter(&self) -> impl Iterator<Item = Entry> + '_ {
697 self.entries()
698 }
699
700 pub fn get_entry(&self, idx: usize) -> Option<Entry> {
702 self.entries().nth(idx)
703 }
704
705 pub fn remove_entry(&mut self, idx: usize) -> Entry {
707 let mut entry = self.get_entry(idx).unwrap();
708 entry.remove();
709 entry
710 }
711
712 fn collect_whitespace(start: Option<NodeOrToken<SyntaxNode, SyntaxToken>>) -> String {
714 let mut pattern = String::new();
715 let mut current = start;
716 while let Some(token) = current {
717 if matches!(token.kind(), WHITESPACE | NEWLINE) {
718 if let NodeOrToken::Token(t) = &token {
719 pattern.push_str(t.text());
720 }
721 current = token.next_sibling_or_token();
722 } else {
723 break;
724 }
725 }
726 pattern
727 }
728
729 fn to_green(node: &NodeOrToken<SyntaxNode, SyntaxToken>) -> NodeOrToken<GreenNode, GreenToken> {
731 match node {
732 NodeOrToken::Node(n) => NodeOrToken::Node(n.green().into()),
733 NodeOrToken::Token(t) => NodeOrToken::Token(t.green().to_owned()),
734 }
735 }
736
737 fn is_whitespace_token(token: &GreenToken) -> bool {
739 token.kind() == rowan::SyntaxKind(WHITESPACE as u16)
740 || token.kind() == rowan::SyntaxKind(NEWLINE as u16)
741 }
742
743 fn strip_trailing_ws_from_children(
745 mut children: Vec<NodeOrToken<GreenNode, GreenToken>>,
746 ) -> Vec<NodeOrToken<GreenNode, GreenToken>> {
747 while let Some(last) = children.last() {
748 if let NodeOrToken::Token(t) = last {
749 if Self::is_whitespace_token(t) {
750 children.pop();
751 } else {
752 break;
753 }
754 } else {
755 break;
756 }
757 }
758 children
759 }
760
761 fn strip_relation_trailing_ws(relation: &SyntaxNode) -> GreenNode {
763 let children: Vec<_> = relation
764 .children_with_tokens()
765 .map(|c| Self::to_green(&c))
766 .collect();
767 let stripped = Self::strip_trailing_ws_from_children(children);
768 GreenNode::new(relation.kind().into(), stripped)
769 }
770
771 fn build_odd_syntax_nodes(
773 before_ws: &str,
774 after_ws: &str,
775 ) -> Vec<NodeOrToken<GreenNode, GreenToken>> {
776 [
777 (!before_ws.is_empty())
778 .then(|| NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), before_ws))),
779 Some(NodeOrToken::Token(GreenToken::new(COMMA.into(), ","))),
780 (!after_ws.is_empty())
781 .then(|| NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), after_ws))),
782 ]
783 .into_iter()
784 .flatten()
785 .collect()
786 }
787
788 fn detect_odd_syntax(&self) -> Option<(String, String)> {
790 for entry_node in self.entries() {
791 let mut node = entry_node.0.next_sibling_or_token()?;
792
793 let mut before = String::new();
795 while matches!(node.kind(), WHITESPACE | NEWLINE) {
796 if let NodeOrToken::Token(t) = &node {
797 before.push_str(t.text());
798 }
799 node = node.next_sibling_or_token()?;
800 }
801
802 if node.kind() == COMMA && !before.is_empty() {
804 let after = Self::collect_whitespace(node.next_sibling_or_token());
805 return Some((before, after));
806 }
807 }
808 None
809 }
810
811 fn detect_whitespace_pattern(&self, default: &str) -> String {
819 use std::collections::HashMap;
820
821 let entries: Vec<_> = self.entries().collect();
822 let num_entries = entries.len();
823
824 if num_entries == 0 {
825 if self.substvars().next().is_some() {
827 return default.to_string();
829 }
830 return String::from(""); }
832
833 if num_entries == 1 {
834 if let Some(node) = entries[0].0.next_sibling_or_token() {
836 if node.kind() == COMMA {
837 let pattern = Self::collect_whitespace(node.next_sibling_or_token());
838 if !pattern.is_empty() {
839 return pattern;
840 }
841 }
842 }
843 return default.to_string(); }
845
846 let mut whitespace_counts: HashMap<String, usize> = HashMap::new();
848
849 for (i, entry) in entries.iter().enumerate() {
850 if i == num_entries - 1 {
851 break; }
853
854 if let Some(mut node) = entry.0.next_sibling_or_token() {
856 while matches!(node.kind(), WHITESPACE | NEWLINE) {
858 if let Some(next) = node.next_sibling_or_token() {
859 node = next;
860 } else {
861 break;
862 }
863 }
864
865 if node.kind() == COMMA {
867 let pattern = Self::collect_whitespace(node.next_sibling_or_token());
868 if !pattern.is_empty() {
869 *whitespace_counts.entry(pattern).or_insert(0) += 1;
870 }
871 }
872 }
873 }
874
875 if whitespace_counts.len() == 1 {
877 if let Some((ws, _)) = whitespace_counts.iter().next() {
878 return ws.clone();
879 }
880 }
881
882 if let Some((ws, _)) = whitespace_counts.iter().max_by_key(|(_, count)| *count) {
884 return ws.clone();
885 }
886
887 default.to_string()
889 }
890
891 pub fn insert_with_separator(&mut self, idx: usize, entry: Entry, default_sep: Option<&str>) {
898 let is_empty = self.entries().next().is_none();
899 let whitespace = self.detect_whitespace_pattern(default_sep.unwrap_or(" "));
900
901 self.strip_trailing_whitespace();
903
904 let odd_syntax = self.detect_odd_syntax();
906
907 let (position, new_children) = if let Some(current_entry) = self.entries().nth(idx) {
908 let to_insert = if idx == 0 && is_empty {
909 vec![entry.0.green().into()]
910 } else if let Some((before_ws, after_ws)) = &odd_syntax {
911 let mut nodes = vec![entry.0.green().into()];
912 nodes.extend(Self::build_odd_syntax_nodes(before_ws, after_ws));
913 nodes
914 } else {
915 vec![
916 entry.0.green().into(),
917 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
918 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), whitespace.as_str())),
919 ]
920 };
921
922 (current_entry.0.index(), to_insert)
923 } else {
924 let child_count = self.0.children_with_tokens().count();
925 let to_insert = if idx == 0 {
926 vec![entry.0.green().into()]
927 } else if let Some((before_ws, after_ws)) = &odd_syntax {
928 let mut nodes = Self::build_odd_syntax_nodes(before_ws, after_ws);
929 nodes.push(entry.0.green().into());
930 nodes
931 } else {
932 vec![
933 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
934 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), whitespace.as_str())),
935 entry.0.green().into(),
936 ]
937 };
938
939 (child_count, to_insert)
940 };
941 self.0 = SyntaxNode::new_root_mut(
943 self.0.replace_with(
944 self.0
945 .green()
946 .splice_children(position..position, new_children),
947 ),
948 );
949 }
950
951 pub fn insert(&mut self, idx: usize, entry: Entry) {
953 self.insert_with_separator(idx, entry, None);
954 }
955
956 fn strip_entry_trailing_ws(entry: &SyntaxNode) -> GreenNode {
958 let mut children: Vec<_> = entry
959 .children_with_tokens()
960 .map(|c| Self::to_green(&c))
961 .collect();
962
963 if let Some(NodeOrToken::Node(last)) = children.last() {
965 if last.kind() == rowan::SyntaxKind(RELATION as u16) {
966 let relation_node = entry.children().last().unwrap();
968 children.pop();
969 children.push(NodeOrToken::Node(Self::strip_relation_trailing_ws(
970 &relation_node,
971 )));
972 }
973 }
974
975 let stripped = Self::strip_trailing_ws_from_children(children);
977 GreenNode::new(ENTRY.into(), stripped)
978 }
979
980 fn strip_trailing_whitespace(&mut self) {
981 let mut children: Vec<_> = self
982 .0
983 .children_with_tokens()
984 .map(|c| Self::to_green(&c))
985 .collect();
986
987 if let Some(NodeOrToken::Node(last)) = children.last() {
989 if last.kind() == rowan::SyntaxKind(ENTRY as u16) {
990 let last_entry = self.0.children().last().unwrap();
991 children.pop();
992 children.push(NodeOrToken::Node(Self::strip_entry_trailing_ws(
993 &last_entry,
994 )));
995 }
996 }
997
998 let stripped = Self::strip_trailing_ws_from_children(children);
1000
1001 let nc = self.0.children_with_tokens().count();
1002 self.0 = SyntaxNode::new_root_mut(
1003 self.0
1004 .replace_with(self.0.green().splice_children(0..nc, stripped)),
1005 );
1006 }
1007
1008 pub fn replace(&mut self, idx: usize, entry: Entry) {
1010 let current_entry = self.get_entry(idx).unwrap();
1011 self.0.splice_children(
1012 current_entry.0.index()..current_entry.0.index() + 1,
1013 vec![entry.0.into()],
1014 );
1015 }
1016
1017 pub fn push(&mut self, entry: Entry) {
1019 let pos = self.entries().count();
1020 self.insert(pos, entry);
1021 }
1022
1023 pub fn substvars(&self) -> impl Iterator<Item = String> + '_ {
1025 self.0
1026 .children()
1027 .filter_map(Substvar::cast)
1028 .map(|s| s.to_string())
1029 }
1030
1031 pub fn parse_relaxed(s: &str, allow_substvar: bool) -> (Relations, Vec<String>) {
1033 let parse = parse(s, allow_substvar);
1034 (parse.root_mut(), parse.errors)
1035 }
1036
1037 pub fn satisfied_by(&self, package_version: impl crate::VersionLookup + Copy) -> bool {
1039 self.entries().all(|e| e.satisfied_by(package_version))
1040 }
1041
1042 pub fn is_empty(&self) -> bool {
1044 self.entries().count() == 0
1045 }
1046
1047 pub fn len(&self) -> usize {
1049 self.entries().count()
1050 }
1051
1052 pub fn ensure_minimum_version(&mut self, package: &str, minimum_version: &Version) {
1075 let mut found = false;
1076 let mut obsolete_indices = vec![];
1077 let mut update_idx = None;
1078
1079 let entries: Vec<_> = self.entries().collect();
1080 for (idx, entry) in entries.iter().enumerate() {
1081 let relations: Vec<_> = entry.relations().collect();
1082
1083 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1085 if names.len() > 1 && names.contains(&package.to_string()) {
1086 let is_obsolete = relations.iter().any(|r| {
1088 if r.name() != package {
1089 return false;
1090 }
1091 if let Some((vc, ver)) = r.version() {
1092 matches!(vc, VersionConstraint::GreaterThan if &ver < minimum_version)
1093 || matches!(vc, VersionConstraint::GreaterThanEqual if &ver <= minimum_version)
1094 } else {
1095 false
1096 }
1097 });
1098 if is_obsolete {
1099 obsolete_indices.push(idx);
1100 }
1101 continue;
1102 }
1103
1104 if names.len() == 1 && names[0] == package {
1106 found = true;
1107 let relation = relations.into_iter().next().unwrap();
1108
1109 let should_update = if let Some((vc, ver)) = relation.version() {
1111 match vc {
1112 VersionConstraint::GreaterThanEqual | VersionConstraint::GreaterThan => {
1113 &ver < minimum_version
1114 }
1115 _ => false,
1116 }
1117 } else {
1118 true
1119 };
1120
1121 if should_update {
1122 update_idx = Some(idx);
1123 }
1124 break;
1125 }
1126 }
1127
1128 if let Some(idx) = update_idx {
1130 let relation = Relation::new(
1131 package,
1132 Some((VersionConstraint::GreaterThanEqual, minimum_version.clone())),
1133 );
1134 let mut entry = self.get_entry(idx).unwrap();
1136 entry.replace(0, relation);
1137 self.replace(idx, entry);
1138 }
1139
1140 for idx in obsolete_indices.into_iter().rev() {
1142 self.remove_entry(idx);
1143 }
1144
1145 if !found {
1147 let relation = Relation::new(
1148 package,
1149 Some((VersionConstraint::GreaterThanEqual, minimum_version.clone())),
1150 );
1151 self.push(Entry::from(relation));
1152 }
1153 }
1154
1155 pub fn ensure_exact_version(&mut self, package: &str, version: &Version) {
1170 let mut found = false;
1171 let mut update_idx = None;
1172
1173 let entries: Vec<_> = self.entries().collect();
1174 for (idx, entry) in entries.iter().enumerate() {
1175 let relations: Vec<_> = entry.relations().collect();
1176 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1177
1178 if names.len() > 1 && names[0] == package {
1179 panic!("Complex rule for {}, aborting", package);
1180 }
1181
1182 if names.len() == 1 && names[0] == package {
1183 found = true;
1184 let relation = relations.into_iter().next().unwrap();
1185
1186 let should_update = if let Some((vc, ver)) = relation.version() {
1187 vc != VersionConstraint::Equal || &ver != version
1188 } else {
1189 true
1190 };
1191
1192 if should_update {
1193 update_idx = Some(idx);
1194 }
1195 break;
1196 }
1197 }
1198
1199 if let Some(idx) = update_idx {
1201 let relation =
1202 Relation::new(package, Some((VersionConstraint::Equal, version.clone())));
1203 let mut entry = self.get_entry(idx).unwrap();
1205 entry.replace(0, relation);
1206 self.replace(idx, entry);
1207 }
1208
1209 if !found {
1210 let relation =
1211 Relation::new(package, Some((VersionConstraint::Equal, version.clone())));
1212 self.push(Entry::from(relation));
1213 }
1214 }
1215
1216 pub fn ensure_some_version(&mut self, package: &str) {
1234 for entry in self.entries() {
1235 let relations: Vec<_> = entry.relations().collect();
1236 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1237
1238 if names.len() > 1 && names[0] == package {
1239 panic!("Complex rule for {}, aborting", package);
1240 }
1241
1242 if names.len() == 1 && names[0] == package {
1243 return;
1245 }
1246 }
1247
1248 let relation = Relation::simple(package);
1250 self.push(Entry::from(relation));
1251 }
1252
1253 pub fn ensure_relation(&mut self, new_entry: Entry) -> bool {
1278 let mut to_replace: Vec<usize> = Vec::new();
1279 let mut to_remove: Vec<usize> = Vec::new();
1280 let mut already_satisfied = false;
1281
1282 for (idx, existing_entry) in self.entries().enumerate() {
1284 if new_entry.is_implied_by(&existing_entry) {
1285 already_satisfied = true;
1287 break;
1288 }
1289 if existing_entry.is_implied_by(&new_entry) {
1290 if to_replace.is_empty() {
1293 to_replace.push(idx);
1294 } else {
1295 to_remove.push(idx);
1296 }
1297 }
1298 }
1299
1300 if already_satisfied {
1301 return false;
1302 }
1303
1304 for idx in to_remove.into_iter().rev() {
1306 self.remove_entry(idx);
1307 }
1308
1309 if let Some(&idx) = to_replace.first() {
1311 self.replace(idx, new_entry);
1312 } else {
1313 self.add_dependency(new_entry, None);
1314 }
1315
1316 true
1317 }
1318
1319 pub fn ensure_substvar(&mut self, substvar: &str) -> Result<(), String> {
1339 for existing in self.substvars() {
1341 if existing.trim() == substvar.trim() {
1342 return Ok(());
1343 }
1344 }
1345
1346 let (parsed, errors) = Relations::parse_relaxed(substvar, true);
1348 if !errors.is_empty() {
1349 return Err(errors.join("\n"));
1350 }
1351
1352 let whitespace = self.detect_whitespace_pattern(" ");
1354
1355 for substvar_node in parsed.0.children().filter(|n| n.kind() == SUBSTVAR) {
1357 let has_content = self.entries().next().is_some() || self.substvars().next().is_some();
1358
1359 let mut builder = GreenNodeBuilder::new();
1360 builder.start_node(ROOT.into());
1361
1362 for child in self.0.children_with_tokens() {
1364 match child {
1365 NodeOrToken::Node(n) => inject(&mut builder, n),
1366 NodeOrToken::Token(t) => builder.token(t.kind().into(), t.text()),
1367 }
1368 }
1369
1370 if has_content {
1372 builder.token(COMMA.into(), ",");
1373 builder.token(WHITESPACE.into(), whitespace.as_str());
1374 }
1375
1376 inject(&mut builder, substvar_node);
1378
1379 builder.finish_node();
1380 self.0 = SyntaxNode::new_root_mut(builder.finish());
1381 }
1382
1383 Ok(())
1384 }
1385
1386 pub fn drop_substvar(&mut self, substvar: &str) {
1403 let substvars_to_remove: Vec<_> = self
1405 .0
1406 .children()
1407 .filter_map(Substvar::cast)
1408 .filter(|s| s.to_string().trim() == substvar.trim())
1409 .collect();
1410
1411 for substvar_node in substvars_to_remove {
1412 let is_first = !substvar_node
1414 .0
1415 .siblings(Direction::Prev)
1416 .skip(1)
1417 .any(|n| n.kind() == ENTRY || n.kind() == SUBSTVAR);
1418
1419 let mut removed_comma = false;
1420
1421 while let Some(n) = substvar_node.0.next_sibling_or_token() {
1423 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1424 n.detach();
1425 } else if n.kind() == COMMA {
1426 n.detach();
1427 removed_comma = true;
1428 break;
1429 } else {
1430 break;
1431 }
1432 }
1433
1434 if !is_first {
1436 while let Some(n) = substvar_node.0.prev_sibling_or_token() {
1437 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1438 n.detach();
1439 } else if !removed_comma && n.kind() == COMMA {
1440 n.detach();
1441 break;
1442 } else {
1443 break;
1444 }
1445 }
1446 } else {
1447 while let Some(n) = substvar_node.0.next_sibling_or_token() {
1449 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1450 n.detach();
1451 } else {
1452 break;
1453 }
1454 }
1455 }
1456
1457 substvar_node.0.detach();
1459 }
1460 }
1461
1462 pub fn filter_entries<F>(&mut self, keep: F)
1478 where
1479 F: Fn(&Entry) -> bool,
1480 {
1481 let indices_to_remove: Vec<_> = self
1482 .entries()
1483 .enumerate()
1484 .filter_map(|(idx, entry)| if keep(&entry) { None } else { Some(idx) })
1485 .collect();
1486
1487 for idx in indices_to_remove.into_iter().rev() {
1489 self.remove_entry(idx);
1490 }
1491 }
1492
1493 pub fn is_sorted(&self, sorting_order: &impl SortingOrder) -> bool {
1509 let mut last_name: Option<String> = None;
1510 for entry in self.entries() {
1511 let mut relations = entry.relations();
1513 let Some(relation) = relations.next() else {
1514 continue;
1515 };
1516
1517 let name = relation.name();
1518
1519 if sorting_order.ignore(&name) {
1521 continue;
1522 }
1523
1524 if let Some(ref last) = last_name {
1526 if sorting_order.lt(&name, last) {
1527 return false;
1528 }
1529 }
1530
1531 last_name = Some(name);
1532 }
1533 true
1534 }
1535
1536 fn find_insert_position(&self, entry: &Entry) -> usize {
1548 let Some(relation) = entry.relations().next() else {
1550 return self.len();
1552 };
1553 let package_name = relation.name();
1554
1555 let count = self.entries().filter(|e| !e.is_empty()).count();
1557
1558 let sorting_order: Box<dyn SortingOrder> = if count < 2 {
1560 Box::new(WrapAndSortOrder)
1561 } else {
1562 if self.is_sorted(&WrapAndSortOrder) {
1565 Box::new(WrapAndSortOrder)
1566 } else if self.is_sorted(&DefaultSortingOrder) {
1567 Box::new(DefaultSortingOrder)
1568 } else {
1569 return self.len();
1571 }
1572 };
1573
1574 if sorting_order.ignore(&package_name) {
1576 return self.len();
1577 }
1578
1579 let mut position = 0;
1581 for (idx, existing_entry) in self.entries().enumerate() {
1582 let mut existing_relations = existing_entry.relations();
1583 let Some(existing_relation) = existing_relations.next() else {
1584 position += 1;
1586 continue;
1587 };
1588
1589 let existing_name = existing_relation.name();
1590
1591 if sorting_order.ignore(&existing_name) {
1593 position += 1;
1594 continue;
1595 }
1596
1597 if sorting_order.lt(&package_name, &existing_name) {
1599 return idx;
1600 }
1601 position += 1;
1602 }
1603
1604 position
1605 }
1606
1607 pub fn drop_dependency(&mut self, package: &str) -> bool {
1625 let indices_to_remove: Vec<_> = self
1626 .entries()
1627 .enumerate()
1628 .filter_map(|(idx, entry)| {
1629 let relations: Vec<_> = entry.relations().collect();
1630 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1631 if names == vec![package] {
1632 Some(idx)
1633 } else {
1634 None
1635 }
1636 })
1637 .collect();
1638
1639 let found = !indices_to_remove.is_empty();
1640
1641 for idx in indices_to_remove.into_iter().rev() {
1643 self.remove_entry(idx);
1644 }
1645
1646 found
1647 }
1648
1649 pub fn add_dependency(&mut self, entry: Entry, position: Option<usize>) {
1670 let pos = position.unwrap_or_else(|| self.find_insert_position(&entry));
1671 self.insert(pos, entry);
1672 }
1673
1674 pub fn get_relation(&self, package: &str) -> Result<(usize, Entry), String> {
1698 for (idx, entry) in self.entries().enumerate() {
1699 let relations: Vec<_> = entry.relations().collect();
1700 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1701
1702 if names.len() > 1 && names.contains(&package.to_string()) {
1703 return Err(format!("Complex rule for {}, aborting", package));
1704 }
1705
1706 if names.len() == 1 && names[0] == package {
1707 return Ok((idx, entry));
1708 }
1709 }
1710 Err(format!("Package {} not found", package))
1711 }
1712
1713 pub fn iter_relations_for(&self, package: &str) -> impl Iterator<Item = (usize, Entry)> + '_ {
1730 let package = package.to_string();
1731 self.entries().enumerate().filter(move |(_, entry)| {
1732 let names: Vec<_> = entry.relations().map(|r| r.name()).collect();
1733 names.contains(&package)
1734 })
1735 }
1736
1737 pub fn has_relation(&self, package: &str) -> bool {
1754 self.entries()
1755 .any(|entry| entry.relations().any(|r| r.name() == package))
1756 }
1757}
1758
1759impl From<Vec<Entry>> for Relations {
1760 fn from(entries: Vec<Entry>) -> Self {
1761 let mut builder = GreenNodeBuilder::new();
1762 builder.start_node(ROOT.into());
1763 for (i, entry) in entries.into_iter().enumerate() {
1764 if i > 0 {
1765 builder.token(COMMA.into(), ",");
1766 builder.token(WHITESPACE.into(), " ");
1767 }
1768 inject(&mut builder, entry.0);
1769 }
1770 builder.finish_node();
1771 Relations(SyntaxNode::new_root_mut(builder.finish()))
1772 }
1773}
1774
1775impl From<Entry> for Relations {
1776 fn from(entry: Entry) -> Self {
1777 Self::from(vec![entry])
1778 }
1779}
1780
1781impl Default for Entry {
1782 fn default() -> Self {
1783 Self::new()
1784 }
1785}
1786
1787impl PartialOrd for Entry {
1788 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1789 Some(self.cmp(other))
1790 }
1791}
1792
1793impl Eq for Entry {}
1794
1795impl Ord for Entry {
1796 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1797 let mut rels_a = self.relations();
1798 let mut rels_b = other.relations();
1799 while let (Some(a), Some(b)) = (rels_a.next(), rels_b.next()) {
1800 match a.cmp(&b) {
1801 std::cmp::Ordering::Equal => continue,
1802 x => return x,
1803 }
1804 }
1805
1806 if rels_a.next().is_some() {
1807 return std::cmp::Ordering::Greater;
1808 }
1809
1810 if rels_b.next().is_some() {
1811 return std::cmp::Ordering::Less;
1812 }
1813
1814 std::cmp::Ordering::Equal
1815 }
1816}
1817
1818impl Entry {
1819 pub fn new() -> Self {
1821 let mut builder = GreenNodeBuilder::new();
1822 builder.start_node(SyntaxKind::ENTRY.into());
1823 builder.finish_node();
1824 Entry(SyntaxNode::new_root_mut(builder.finish()))
1825 }
1826
1827 pub fn replace(&mut self, idx: usize, relation: Relation) {
1829 let current_relation = self.get_relation(idx).unwrap();
1830
1831 let old_root = current_relation.0;
1832 let new_root = relation.0;
1833 let mut prev = new_root.first_child_or_token();
1835 let mut new_head_len = 0;
1836 while let Some(p) = prev {
1838 if p.kind() == WHITESPACE || p.kind() == NEWLINE {
1839 new_head_len += 1;
1840 prev = p.next_sibling_or_token();
1841 } else {
1842 break;
1843 }
1844 }
1845 let mut new_tail_len = 0;
1846 let mut next = new_root.last_child_or_token();
1847 while let Some(n) = next {
1848 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1849 new_tail_len += 1;
1850 next = n.prev_sibling_or_token();
1851 } else {
1852 break;
1853 }
1854 }
1855 let mut prev = old_root.first_child_or_token();
1857 let mut old_head = vec![];
1858 while let Some(p) = prev {
1859 if p.kind() == WHITESPACE || p.kind() == NEWLINE {
1860 old_head.push(p.clone());
1861 prev = p.next_sibling_or_token();
1862 } else {
1863 break;
1864 }
1865 }
1866 let mut old_tail = vec![];
1867 let mut next = old_root.last_child_or_token();
1868 while let Some(n) = next {
1869 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1870 old_tail.push(n.clone());
1871 next = n.prev_sibling_or_token();
1872 } else {
1873 break;
1874 }
1875 }
1876 new_root.splice_children(0..new_head_len, old_head);
1877 let tail_pos = new_root.children_with_tokens().count() - new_tail_len;
1878 new_root.splice_children(
1879 tail_pos - new_tail_len..tail_pos,
1880 old_tail.into_iter().rev(),
1881 );
1882 let index = old_root.index();
1883 self.0
1884 .splice_children(index..index + 1, vec![new_root.into()]);
1885 }
1886
1887 #[must_use]
1889 pub fn wrap_and_sort(&self) -> Self {
1890 let mut relations = self
1891 .relations()
1892 .map(|r| r.wrap_and_sort())
1893 .collect::<Vec<_>>();
1894 relations.sort();
1896 Self::from(relations)
1897 }
1898
1899 pub fn relations(&self) -> impl Iterator<Item = Relation> + '_ {
1901 self.0.children().filter_map(Relation::cast)
1902 }
1903
1904 pub fn iter(&self) -> impl Iterator<Item = Relation> + '_ {
1906 self.relations()
1907 }
1908
1909 pub fn get_relation(&self, idx: usize) -> Option<Relation> {
1911 self.relations().nth(idx)
1912 }
1913
1914 pub fn remove_relation(&self, idx: usize) -> Relation {
1927 let mut relation = self.get_relation(idx).unwrap();
1928 relation.remove();
1929 relation
1930 }
1931
1932 pub fn satisfied_by(&self, package_version: impl crate::VersionLookup + Copy) -> bool {
1948 self.relations().any(|r| {
1949 let actual = package_version.lookup_version(r.name().as_str());
1950 if let Some((vc, version)) = r.version() {
1951 if let Some(actual) = actual {
1952 match vc {
1953 VersionConstraint::GreaterThanEqual => *actual >= version,
1954 VersionConstraint::LessThanEqual => *actual <= version,
1955 VersionConstraint::Equal => *actual == version,
1956 VersionConstraint::GreaterThan => *actual > version,
1957 VersionConstraint::LessThan => *actual < version,
1958 }
1959 } else {
1960 false
1961 }
1962 } else {
1963 actual.is_some()
1964 }
1965 })
1966 }
1967
1968 pub fn remove(&mut self) {
1979 let mut removed_comma = false;
1980 let is_first = !self
1981 .0
1982 .siblings(Direction::Prev)
1983 .skip(1)
1984 .any(|n| n.kind() == ENTRY);
1985 while let Some(n) = self.0.next_sibling_or_token() {
1986 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1987 n.detach();
1988 } else if n.kind() == COMMA {
1989 n.detach();
1990 removed_comma = true;
1991 break;
1992 } else {
1993 panic!("Unexpected node: {:?}", n);
1994 }
1995 }
1996 if !is_first {
1997 while let Some(n) = self.0.prev_sibling_or_token() {
1998 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1999 n.detach();
2000 } else if !removed_comma && n.kind() == COMMA {
2001 n.detach();
2002 break;
2003 } else {
2004 break;
2005 }
2006 }
2007 } else {
2008 while let Some(n) = self.0.next_sibling_or_token() {
2009 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2010 n.detach();
2011 } else {
2012 break;
2013 }
2014 }
2015 }
2016 self.0.detach();
2017 }
2018
2019 pub fn is_empty(&self) -> bool {
2021 self.relations().count() == 0
2022 }
2023
2024 pub fn len(&self) -> usize {
2026 self.relations().count()
2027 }
2028
2029 pub fn push(&mut self, relation: Relation) {
2042 let is_empty = !self
2043 .0
2044 .children_with_tokens()
2045 .any(|n| n.kind() == PIPE || n.kind() == RELATION);
2046
2047 let (position, new_children) = if let Some(current_relation) = self.relations().last() {
2048 let to_insert: Vec<NodeOrToken<GreenNode, GreenToken>> = if is_empty {
2049 vec![relation.0.green().into()]
2050 } else {
2051 vec![
2052 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
2053 NodeOrToken::Token(GreenToken::new(PIPE.into(), "|")),
2054 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
2055 relation.0.green().into(),
2056 ]
2057 };
2058
2059 (current_relation.0.index() + 1, to_insert)
2060 } else {
2061 let child_count = self.0.children_with_tokens().count();
2062 (
2063 child_count,
2064 if is_empty {
2065 vec![relation.0.green().into()]
2066 } else {
2067 vec![
2068 NodeOrToken::Token(GreenToken::new(PIPE.into(), "|")),
2069 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
2070 relation.0.green().into(),
2071 ]
2072 },
2073 )
2074 };
2075
2076 let new_root = SyntaxNode::new_root_mut(
2077 self.0.replace_with(
2078 self.0
2079 .green()
2080 .splice_children(position..position, new_children),
2081 ),
2082 );
2083
2084 if let Some(parent) = self.0.parent() {
2085 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2086 self.0 = parent
2087 .children_with_tokens()
2088 .nth(self.0.index())
2089 .unwrap()
2090 .clone()
2091 .into_node()
2092 .unwrap();
2093 } else {
2094 self.0 = new_root;
2095 }
2096 }
2097
2098 pub fn is_implied_by(&self, outer: &Entry) -> bool {
2123 if self == outer {
2125 return true;
2126 }
2127
2128 for inner_rel in self.relations() {
2130 for outer_rel in outer.relations() {
2131 if inner_rel.is_implied_by(&outer_rel) {
2132 return true;
2133 }
2134 }
2135 }
2136
2137 false
2138 }
2139}
2140
2141fn inject(builder: &mut GreenNodeBuilder, node: SyntaxNode) {
2142 builder.start_node(node.kind().into());
2143 for child in node.children_with_tokens() {
2144 match child {
2145 rowan::NodeOrToken::Node(child) => {
2146 inject(builder, child);
2147 }
2148 rowan::NodeOrToken::Token(token) => {
2149 builder.token(token.kind().into(), token.text());
2150 }
2151 }
2152 }
2153 builder.finish_node();
2154}
2155
2156impl From<Vec<Relation>> for Entry {
2157 fn from(relations: Vec<Relation>) -> Self {
2158 let mut builder = GreenNodeBuilder::new();
2159 builder.start_node(SyntaxKind::ENTRY.into());
2160 for (i, relation) in relations.into_iter().enumerate() {
2161 if i > 0 {
2162 builder.token(WHITESPACE.into(), " ");
2163 builder.token(COMMA.into(), "|");
2164 builder.token(WHITESPACE.into(), " ");
2165 }
2166 inject(&mut builder, relation.0);
2167 }
2168 builder.finish_node();
2169 Entry(SyntaxNode::new_root_mut(builder.finish()))
2170 }
2171}
2172
2173impl From<Relation> for Entry {
2174 fn from(relation: Relation) -> Self {
2175 Self::from(vec![relation])
2176 }
2177}
2178
2179fn tokenize_version(builder: &mut GreenNodeBuilder, version: &Version) {
2182 let version_str = version.to_string();
2183
2184 if let Some(colon_pos) = version_str.find(':') {
2186 builder.token(IDENT.into(), &version_str[..colon_pos]);
2188 builder.token(COLON.into(), ":");
2189 builder.token(IDENT.into(), &version_str[colon_pos + 1..]);
2191 } else {
2192 builder.token(IDENT.into(), version_str.as_str());
2194 }
2195}
2196
2197impl Relation {
2198 pub fn new(name: &str, version_constraint: Option<(VersionConstraint, Version)>) -> Self {
2212 let mut builder = GreenNodeBuilder::new();
2213 builder.start_node(SyntaxKind::RELATION.into());
2214 builder.token(IDENT.into(), name);
2215 if let Some((vc, version)) = version_constraint {
2216 builder.token(WHITESPACE.into(), " ");
2217 builder.start_node(SyntaxKind::VERSION.into());
2218 builder.token(L_PARENS.into(), "(");
2219 builder.start_node(SyntaxKind::CONSTRAINT.into());
2220 for c in vc.to_string().chars() {
2221 builder.token(
2222 match c {
2223 '>' => R_ANGLE.into(),
2224 '<' => L_ANGLE.into(),
2225 '=' => EQUAL.into(),
2226 _ => unreachable!(),
2227 },
2228 c.to_string().as_str(),
2229 );
2230 }
2231 builder.finish_node();
2232
2233 builder.token(WHITESPACE.into(), " ");
2234
2235 tokenize_version(&mut builder, &version);
2236
2237 builder.token(R_PARENS.into(), ")");
2238
2239 builder.finish_node();
2240 }
2241
2242 builder.finish_node();
2243 Relation(SyntaxNode::new_root_mut(builder.finish()))
2244 }
2245
2246 #[must_use]
2255 pub fn wrap_and_sort(&self) -> Self {
2256 let mut builder = GreenNodeBuilder::new();
2257 builder.start_node(SyntaxKind::RELATION.into());
2258 builder.token(IDENT.into(), self.name().as_str());
2259 if let Some(archqual) = self.archqual() {
2260 builder.token(COLON.into(), ":");
2261 builder.token(IDENT.into(), archqual.as_str());
2262 }
2263 if let Some((vc, version)) = self.version() {
2264 builder.token(WHITESPACE.into(), " ");
2265 builder.start_node(SyntaxKind::VERSION.into());
2266 builder.token(L_PARENS.into(), "(");
2267 builder.start_node(SyntaxKind::CONSTRAINT.into());
2268 builder.token(
2269 match vc {
2270 VersionConstraint::GreaterThanEqual => R_ANGLE.into(),
2271 VersionConstraint::LessThanEqual => L_ANGLE.into(),
2272 VersionConstraint::Equal => EQUAL.into(),
2273 VersionConstraint::GreaterThan => R_ANGLE.into(),
2274 VersionConstraint::LessThan => L_ANGLE.into(),
2275 },
2276 vc.to_string().as_str(),
2277 );
2278 builder.finish_node();
2279 builder.token(WHITESPACE.into(), " ");
2280 tokenize_version(&mut builder, &version);
2281 builder.token(R_PARENS.into(), ")");
2282 builder.finish_node();
2283 }
2284 if let Some(architectures) = self.architectures() {
2285 builder.token(WHITESPACE.into(), " ");
2286 builder.start_node(ARCHITECTURES.into());
2287 builder.token(L_BRACKET.into(), "[");
2288 for (i, arch) in architectures.enumerate() {
2289 if i > 0 {
2290 builder.token(WHITESPACE.into(), " ");
2291 }
2292 builder.token(IDENT.into(), arch.as_str());
2293 }
2294 builder.token(R_BRACKET.into(), "]");
2295 builder.finish_node();
2296 }
2297 for profiles in self.profiles() {
2298 builder.token(WHITESPACE.into(), " ");
2299 builder.start_node(PROFILES.into());
2300 builder.token(L_ANGLE.into(), "<");
2301 for (i, profile) in profiles.into_iter().enumerate() {
2302 if i > 0 {
2303 builder.token(WHITESPACE.into(), " ");
2304 }
2305 match profile {
2306 BuildProfile::Disabled(name) => {
2307 builder.token(NOT.into(), "!");
2308 builder.token(IDENT.into(), name.as_str());
2309 }
2310 BuildProfile::Enabled(name) => {
2311 builder.token(IDENT.into(), name.as_str());
2312 }
2313 }
2314 }
2315 builder.token(R_ANGLE.into(), ">");
2316 builder.finish_node();
2317 }
2318 builder.finish_node();
2319 Relation(SyntaxNode::new_root_mut(builder.finish()))
2320 }
2321
2322 pub fn simple(name: &str) -> Self {
2331 Self::new(name, None)
2332 }
2333
2334 pub fn drop_constraint(&mut self) -> bool {
2345 let version_token = self.0.children().find(|n| n.kind() == VERSION);
2346 if let Some(version_token) = version_token {
2347 while let Some(prev) = version_token.prev_sibling_or_token() {
2349 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
2350 prev.detach();
2351 } else {
2352 break;
2353 }
2354 }
2355 version_token.detach();
2356 return true;
2357 }
2358
2359 false
2360 }
2361
2362 pub fn name(&self) -> String {
2371 self.0
2372 .children_with_tokens()
2373 .find_map(|it| match it {
2374 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2375 _ => None,
2376 })
2377 .unwrap()
2378 .text()
2379 .to_string()
2380 }
2381
2382 pub fn archqual(&self) -> Option<String> {
2391 let archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
2392 let node = if let Some(archqual) = archqual {
2393 archqual.children_with_tokens().find_map(|it| match it {
2394 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2395 _ => None,
2396 })
2397 } else {
2398 None
2399 };
2400 node.map(|n| n.text().to_string())
2401 }
2402
2403 pub fn set_archqual(&mut self, archqual: &str) {
2413 let mut builder = GreenNodeBuilder::new();
2414 builder.start_node(ARCHQUAL.into());
2415 builder.token(COLON.into(), ":");
2416 builder.token(IDENT.into(), archqual);
2417 builder.finish_node();
2418
2419 let node_archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
2420 if let Some(node_archqual) = node_archqual {
2421 self.0.splice_children(
2422 node_archqual.index()..node_archqual.index() + 1,
2423 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2424 );
2425 } else {
2426 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
2427 let idx = if let Some(name_node) = name_node {
2428 name_node.index() + 1
2429 } else {
2430 0
2431 };
2432 self.0.splice_children(
2433 idx..idx,
2434 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2435 );
2436 }
2437 }
2438
2439 pub fn version(&self) -> Option<(VersionConstraint, Version)> {
2441 let vc = self.0.children().find(|n| n.kind() == VERSION);
2442 let vc = vc.as_ref()?;
2443 let constraint = vc.children().find(|n| n.kind() == CONSTRAINT);
2444
2445 let version_str: String = vc
2447 .children_with_tokens()
2448 .filter_map(|it| match it {
2449 SyntaxElement::Token(token) if token.kind() == IDENT || token.kind() == COLON => {
2450 Some(token.text().to_string())
2451 }
2452 _ => None,
2453 })
2454 .collect();
2455
2456 if let Some(constraint) = constraint {
2457 if !version_str.is_empty() {
2458 let vc: VersionConstraint = constraint.to_string().parse().unwrap();
2459 Some((vc, version_str.parse().unwrap()))
2460 } else {
2461 None
2462 }
2463 } else {
2464 None
2465 }
2466 }
2467
2468 pub fn set_version(&mut self, version_constraint: Option<(VersionConstraint, Version)>) {
2479 let current_version = self.0.children().find(|n| n.kind() == VERSION);
2480 if let Some((vc, version)) = version_constraint {
2481 let mut builder = GreenNodeBuilder::new();
2482 builder.start_node(VERSION.into());
2483 builder.token(L_PARENS.into(), "(");
2484 builder.start_node(CONSTRAINT.into());
2485 match vc {
2486 VersionConstraint::GreaterThanEqual => {
2487 builder.token(R_ANGLE.into(), ">");
2488 builder.token(EQUAL.into(), "=");
2489 }
2490 VersionConstraint::LessThanEqual => {
2491 builder.token(L_ANGLE.into(), "<");
2492 builder.token(EQUAL.into(), "=");
2493 }
2494 VersionConstraint::Equal => {
2495 builder.token(EQUAL.into(), "=");
2496 }
2497 VersionConstraint::GreaterThan => {
2498 builder.token(R_ANGLE.into(), ">");
2499 }
2500 VersionConstraint::LessThan => {
2501 builder.token(L_ANGLE.into(), "<");
2502 }
2503 }
2504 builder.finish_node(); builder.token(WHITESPACE.into(), " ");
2506 tokenize_version(&mut builder, &version);
2507 builder.token(R_PARENS.into(), ")");
2508 builder.finish_node(); if let Some(current_version) = current_version {
2511 self.0.splice_children(
2512 current_version.index()..current_version.index() + 1,
2513 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2514 );
2515 } else {
2516 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
2517 let idx = if let Some(name_node) = name_node {
2518 name_node.index() + 1
2519 } else {
2520 0
2521 };
2522 let new_children = vec![
2523 GreenToken::new(WHITESPACE.into(), " ").into(),
2524 builder.finish().into(),
2525 ];
2526 let new_root = SyntaxNode::new_root_mut(
2527 self.0.green().splice_children(idx..idx, new_children),
2528 );
2529 if let Some(parent) = self.0.parent() {
2530 parent
2531 .splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2532 self.0 = parent
2533 .children_with_tokens()
2534 .nth(self.0.index())
2535 .unwrap()
2536 .clone()
2537 .into_node()
2538 .unwrap();
2539 } else {
2540 self.0 = new_root;
2541 }
2542 }
2543 } else if let Some(current_version) = current_version {
2544 while let Some(prev) = current_version.prev_sibling_or_token() {
2546 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
2547 prev.detach();
2548 } else {
2549 break;
2550 }
2551 }
2552 current_version.detach();
2553 }
2554 }
2555
2556 pub fn architectures(&self) -> Option<impl Iterator<Item = String> + '_> {
2565 let architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES)?;
2566
2567 Some(architectures.children_with_tokens().filter_map(|node| {
2568 let token = node.as_token()?;
2569 if token.kind() == IDENT {
2570 Some(token.text().to_string())
2571 } else {
2572 None
2573 }
2574 }))
2575 }
2576
2577 pub fn profiles(&self) -> impl Iterator<Item = Vec<BuildProfile>> + '_ {
2587 let profiles = self.0.children().filter(|n| n.kind() == PROFILES);
2588
2589 profiles.map(|profile| {
2590 let mut ret = vec![];
2592 let mut current = vec![];
2593 for token in profile.children_with_tokens() {
2594 match token.kind() {
2595 WHITESPACE | NEWLINE => {
2596 if !current.is_empty() {
2597 ret.push(current.join("").parse::<BuildProfile>().unwrap());
2598 current = vec![];
2599 }
2600 }
2601 L_ANGLE | R_ANGLE => {}
2602 _ => {
2603 current.push(token.to_string());
2604 }
2605 }
2606 }
2607 if !current.is_empty() {
2608 ret.push(current.concat().parse().unwrap());
2609 }
2610 ret
2611 })
2612 }
2613
2614 pub fn remove(&mut self) {
2625 let is_first = !self
2626 .0
2627 .siblings(Direction::Prev)
2628 .skip(1)
2629 .any(|n| n.kind() == RELATION);
2630 if !is_first {
2631 while let Some(n) = self.0.prev_sibling_or_token() {
2634 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2635 n.detach();
2636 } else if n.kind() == PIPE {
2637 n.detach();
2638 break;
2639 } else {
2640 break;
2641 }
2642 }
2643 while let Some(n) = self.0.prev_sibling_or_token() {
2644 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2645 n.detach();
2646 } else {
2647 break;
2648 }
2649 }
2650 } else {
2651 while let Some(n) = self.0.next_sibling_or_token() {
2654 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2655 n.detach();
2656 } else if n.kind() == PIPE {
2657 n.detach();
2658 break;
2659 } else {
2660 panic!("Unexpected node: {:?}", n);
2661 }
2662 }
2663
2664 while let Some(n) = self.0.next_sibling_or_token() {
2665 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2666 n.detach();
2667 } else {
2668 break;
2669 }
2670 }
2671 }
2672 if let Some(mut parent) = self.0.parent().and_then(Entry::cast) {
2674 if parent.is_empty() {
2675 parent.remove();
2676 } else {
2677 self.0.detach();
2678 }
2679 } else {
2680 self.0.detach();
2681 }
2682 }
2683
2684 pub fn set_architectures<'a>(&mut self, architectures: impl Iterator<Item = &'a str>) {
2694 let mut builder = GreenNodeBuilder::new();
2695 builder.start_node(ARCHITECTURES.into());
2696 builder.token(L_BRACKET.into(), "[");
2697 for (i, arch) in architectures.enumerate() {
2698 if i > 0 {
2699 builder.token(WHITESPACE.into(), " ");
2700 }
2701 builder.token(IDENT.into(), arch);
2702 }
2703 builder.token(R_BRACKET.into(), "]");
2704 builder.finish_node();
2705
2706 let node_architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES);
2707 if let Some(node_architectures) = node_architectures {
2708 let new_root = SyntaxNode::new_root_mut(builder.finish());
2709 self.0.splice_children(
2710 node_architectures.index()..node_architectures.index() + 1,
2711 vec![new_root.into()],
2712 );
2713 } else {
2714 let profiles = self.0.children().find(|n| n.kind() == PROFILES);
2715 let idx = if let Some(profiles) = profiles {
2716 profiles.index()
2717 } else {
2718 self.0.children_with_tokens().count()
2719 };
2720 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
2721 idx..idx,
2722 vec![
2723 GreenToken::new(WHITESPACE.into(), " ").into(),
2724 builder.finish().into(),
2725 ],
2726 ));
2727 if let Some(parent) = self.0.parent() {
2728 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2729 self.0 = parent
2730 .children_with_tokens()
2731 .nth(self.0.index())
2732 .unwrap()
2733 .clone()
2734 .into_node()
2735 .unwrap();
2736 } else {
2737 self.0 = new_root;
2738 }
2739 }
2740 }
2741
2742 pub fn add_profile(&mut self, profile: &[BuildProfile]) {
2753 let mut builder = GreenNodeBuilder::new();
2754 builder.start_node(PROFILES.into());
2755 builder.token(L_ANGLE.into(), "<");
2756 for (i, profile) in profile.iter().enumerate() {
2757 if i > 0 {
2758 builder.token(WHITESPACE.into(), " ");
2759 }
2760 match profile {
2761 BuildProfile::Disabled(name) => {
2762 builder.token(NOT.into(), "!");
2763 builder.token(IDENT.into(), name.as_str());
2764 }
2765 BuildProfile::Enabled(name) => {
2766 builder.token(IDENT.into(), name.as_str());
2767 }
2768 }
2769 }
2770 builder.token(R_ANGLE.into(), ">");
2771 builder.finish_node();
2772
2773 let node_profiles = self.0.children().find(|n| n.kind() == PROFILES);
2774 if let Some(node_profiles) = node_profiles {
2775 let new_root = SyntaxNode::new_root_mut(builder.finish());
2776 self.0.splice_children(
2777 node_profiles.index()..node_profiles.index() + 1,
2778 vec![new_root.into()],
2779 );
2780 } else {
2781 let idx = self.0.children_with_tokens().count();
2782 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
2783 idx..idx,
2784 vec![
2785 GreenToken::new(WHITESPACE.into(), " ").into(),
2786 builder.finish().into(),
2787 ],
2788 ));
2789 if let Some(parent) = self.0.parent() {
2790 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2791 self.0 = parent
2792 .children_with_tokens()
2793 .nth(self.0.index())
2794 .unwrap()
2795 .clone()
2796 .into_node()
2797 .unwrap();
2798 } else {
2799 self.0 = new_root;
2800 }
2801 }
2802 }
2803
2804 pub fn build(name: &str) -> RelationBuilder {
2806 RelationBuilder::new(name)
2807 }
2808
2809 pub fn is_implied_by(&self, outer: &Relation) -> bool {
2836 if self.name() != outer.name() {
2837 return false;
2838 }
2839
2840 let inner_version = self.version();
2841 let outer_version = outer.version();
2842
2843 if inner_version.is_none() {
2845 return true;
2846 }
2847
2848 if inner_version == outer_version {
2850 return true;
2851 }
2852
2853 if outer_version.is_none() {
2855 return false;
2856 }
2857
2858 let (inner_constraint, inner_ver) = inner_version.unwrap();
2859 let (outer_constraint, outer_ver) = outer_version.unwrap();
2860
2861 use VersionConstraint::*;
2862 match inner_constraint {
2863 GreaterThanEqual => match outer_constraint {
2864 GreaterThan => outer_ver > inner_ver,
2865 GreaterThanEqual | Equal => outer_ver >= inner_ver,
2866 LessThan | LessThanEqual => false,
2867 },
2868 Equal => match outer_constraint {
2869 Equal => outer_ver == inner_ver,
2870 _ => false,
2871 },
2872 LessThan => match outer_constraint {
2873 LessThan => outer_ver <= inner_ver,
2874 LessThanEqual | Equal => outer_ver < inner_ver,
2875 GreaterThan | GreaterThanEqual => false,
2876 },
2877 LessThanEqual => match outer_constraint {
2878 LessThanEqual | Equal | LessThan => outer_ver <= inner_ver,
2879 GreaterThan | GreaterThanEqual => false,
2880 },
2881 GreaterThan => match outer_constraint {
2882 GreaterThan => outer_ver >= inner_ver,
2883 Equal | GreaterThanEqual => outer_ver > inner_ver,
2884 LessThan | LessThanEqual => false,
2885 },
2886 }
2887 }
2888}
2889
2890pub struct RelationBuilder {
2904 name: String,
2905 version_constraint: Option<(VersionConstraint, Version)>,
2906 archqual: Option<String>,
2907 architectures: Option<Vec<String>>,
2908 profiles: Vec<Vec<BuildProfile>>,
2909}
2910
2911impl RelationBuilder {
2912 fn new(name: &str) -> Self {
2914 Self {
2915 name: name.to_string(),
2916 version_constraint: None,
2917 archqual: None,
2918 architectures: None,
2919 profiles: vec![],
2920 }
2921 }
2922
2923 pub fn version_constraint(mut self, vc: VersionConstraint, version: Version) -> Self {
2925 self.version_constraint = Some((vc, version));
2926 self
2927 }
2928
2929 pub fn archqual(mut self, archqual: &str) -> Self {
2931 self.archqual = Some(archqual.to_string());
2932 self
2933 }
2934
2935 pub fn architectures(mut self, architectures: Vec<String>) -> Self {
2937 self.architectures = Some(architectures);
2938 self
2939 }
2940
2941 pub fn profiles(mut self, profiles: Vec<Vec<BuildProfile>>) -> Self {
2943 self.profiles = profiles;
2944 self
2945 }
2946
2947 pub fn add_profile(mut self, profile: Vec<BuildProfile>) -> Self {
2949 self.profiles.push(profile);
2950 self
2951 }
2952
2953 pub fn build(self) -> Relation {
2955 let mut relation = Relation::new(&self.name, self.version_constraint);
2956 if let Some(archqual) = &self.archqual {
2957 relation.set_archqual(archqual);
2958 }
2959 if let Some(architectures) = &self.architectures {
2960 relation.set_architectures(architectures.iter().map(|s| s.as_str()));
2961 }
2962 for profile in &self.profiles {
2963 relation.add_profile(profile);
2964 }
2965 relation
2966 }
2967}
2968
2969impl PartialOrd for Relation {
2970 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
2971 Some(self.cmp(other))
2972 }
2973}
2974
2975impl Eq for Relation {}
2976
2977impl Ord for Relation {
2978 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
2979 let name_cmp = self.name().cmp(&other.name());
2981 if name_cmp != std::cmp::Ordering::Equal {
2982 return name_cmp;
2983 }
2984
2985 let self_version = self.version();
2986 let other_version = other.version();
2987
2988 match (self_version, other_version) {
2989 (Some((self_vc, self_version)), Some((other_vc, other_version))) => {
2990 let vc_cmp = self_vc.cmp(&other_vc);
2991 if vc_cmp != std::cmp::Ordering::Equal {
2992 return vc_cmp;
2993 }
2994
2995 self_version.cmp(&other_version)
2996 }
2997 (Some(_), None) => std::cmp::Ordering::Greater,
2998 (None, Some(_)) => std::cmp::Ordering::Less,
2999 (None, None) => std::cmp::Ordering::Equal,
3000 }
3001 }
3002}
3003
3004impl std::str::FromStr for Relations {
3005 type Err = String;
3006
3007 fn from_str(s: &str) -> Result<Self, Self::Err> {
3008 let parse = parse(s, false);
3009 if parse.errors.is_empty() {
3010 Ok(parse.root_mut())
3011 } else {
3012 Err(parse.errors.join("\n"))
3013 }
3014 }
3015}
3016
3017impl std::str::FromStr for Entry {
3018 type Err = String;
3019
3020 fn from_str(s: &str) -> Result<Self, Self::Err> {
3021 let root: Relations = s.parse()?;
3022
3023 let mut entries = root.entries();
3024 let entry = if let Some(entry) = entries.next() {
3025 entry
3026 } else {
3027 return Err("No entry found".to_string());
3028 };
3029
3030 if entries.next().is_some() {
3031 return Err("Multiple entries found".to_string());
3032 }
3033
3034 Ok(entry)
3035 }
3036}
3037
3038impl std::str::FromStr for Relation {
3039 type Err = String;
3040
3041 fn from_str(s: &str) -> Result<Self, Self::Err> {
3042 let entry: Entry = s.parse()?;
3043
3044 let mut relations = entry.relations();
3045 let relation = if let Some(relation) = relations.next() {
3046 relation
3047 } else {
3048 return Err("No relation found".to_string());
3049 };
3050
3051 if relations.next().is_some() {
3052 return Err("Multiple relations found".to_string());
3053 }
3054
3055 Ok(relation)
3056 }
3057}
3058
3059impl From<crate::lossy::Relation> for Relation {
3060 fn from(relation: crate::lossy::Relation) -> Self {
3061 let mut builder = Relation::build(&relation.name);
3062
3063 if let Some((vc, version)) = relation.version {
3064 builder = builder.version_constraint(vc, version);
3065 }
3066
3067 if let Some(archqual) = relation.archqual {
3068 builder = builder.archqual(&archqual);
3069 }
3070
3071 if let Some(architectures) = relation.architectures {
3072 builder = builder.architectures(architectures);
3073 }
3074
3075 builder = builder.profiles(relation.profiles);
3076
3077 builder.build()
3078 }
3079}
3080
3081impl From<Relation> for crate::lossy::Relation {
3082 fn from(relation: Relation) -> Self {
3083 crate::lossy::Relation {
3084 name: relation.name(),
3085 version: relation.version(),
3086 archqual: relation.archqual(),
3087 architectures: relation.architectures().map(|a| a.collect()),
3088 profiles: relation.profiles().collect(),
3089 }
3090 }
3091}
3092
3093impl From<Entry> for Vec<crate::lossy::Relation> {
3094 fn from(entry: Entry) -> Self {
3095 entry.relations().map(|r| r.into()).collect()
3096 }
3097}
3098
3099impl From<Vec<crate::lossy::Relation>> for Entry {
3100 fn from(relations: Vec<crate::lossy::Relation>) -> Self {
3101 let relations: Vec<Relation> = relations.into_iter().map(|r| r.into()).collect();
3102 Entry::from(relations)
3103 }
3104}
3105
3106#[cfg(test)]
3107mod tests {
3108 use super::*;
3109
3110 #[test]
3111 fn test_parse() {
3112 let input = "python3-dulwich";
3113 let parsed: Relations = input.parse().unwrap();
3114 assert_eq!(parsed.to_string(), input);
3115 assert_eq!(parsed.entries().count(), 1);
3116 let entry = parsed.entries().next().unwrap();
3117 assert_eq!(entry.to_string(), "python3-dulwich");
3118 assert_eq!(entry.relations().count(), 1);
3119 let relation = entry.relations().next().unwrap();
3120 assert_eq!(relation.to_string(), "python3-dulwich");
3121 assert_eq!(relation.version(), None);
3122
3123 let input = "python3-dulwich (>= 0.20.21)";
3124 let parsed: Relations = input.parse().unwrap();
3125 assert_eq!(parsed.to_string(), input);
3126 assert_eq!(parsed.entries().count(), 1);
3127 let entry = parsed.entries().next().unwrap();
3128 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3129 assert_eq!(entry.relations().count(), 1);
3130 let relation = entry.relations().next().unwrap();
3131 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
3132 assert_eq!(
3133 relation.version(),
3134 Some((
3135 VersionConstraint::GreaterThanEqual,
3136 "0.20.21".parse().unwrap()
3137 ))
3138 );
3139 }
3140
3141 #[test]
3142 fn test_multiple() {
3143 let input = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)";
3144 let parsed: Relations = input.parse().unwrap();
3145 assert_eq!(parsed.to_string(), input);
3146 assert_eq!(parsed.entries().count(), 2);
3147 let entry = parsed.entries().next().unwrap();
3148 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3149 assert_eq!(entry.relations().count(), 1);
3150 let relation = entry.relations().next().unwrap();
3151 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
3152 assert_eq!(
3153 relation.version(),
3154 Some((
3155 VersionConstraint::GreaterThanEqual,
3156 "0.20.21".parse().unwrap()
3157 ))
3158 );
3159 let entry = parsed.entries().nth(1).unwrap();
3160 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.21)");
3161 assert_eq!(entry.relations().count(), 1);
3162 let relation = entry.relations().next().unwrap();
3163 assert_eq!(relation.to_string(), "python3-dulwich (<< 0.21)");
3164 assert_eq!(
3165 relation.version(),
3166 Some((VersionConstraint::LessThan, "0.21".parse().unwrap()))
3167 );
3168 }
3169
3170 #[test]
3171 fn test_architectures() {
3172 let input = "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]";
3173 let parsed: Relations = input.parse().unwrap();
3174 assert_eq!(parsed.to_string(), input);
3175 assert_eq!(parsed.entries().count(), 1);
3176 let entry = parsed.entries().next().unwrap();
3177 assert_eq!(
3178 entry.to_string(),
3179 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
3180 );
3181 assert_eq!(entry.relations().count(), 1);
3182 let relation = entry.relations().next().unwrap();
3183 assert_eq!(
3184 relation.to_string(),
3185 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
3186 );
3187 assert_eq!(relation.version(), None);
3188 assert_eq!(
3189 relation.architectures().unwrap().collect::<Vec<_>>(),
3190 vec![
3191 "amd64", "arm64", "armhf", "i386", "mips", "mips64el", "mipsel", "ppc64el", "s390x"
3192 ]
3193 .into_iter()
3194 .map(|s| s.to_string())
3195 .collect::<Vec<_>>()
3196 );
3197 }
3198
3199 #[test]
3200 fn test_profiles() {
3201 let input = "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>, bar";
3202 let parsed: Relations = input.parse().unwrap();
3203 assert_eq!(parsed.to_string(), input);
3204 assert_eq!(parsed.entries().count(), 2);
3205 let entry = parsed.entries().next().unwrap();
3206 assert_eq!(
3207 entry.to_string(),
3208 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
3209 );
3210 assert_eq!(entry.relations().count(), 1);
3211 let relation = entry.relations().next().unwrap();
3212 assert_eq!(
3213 relation.to_string(),
3214 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
3215 );
3216 assert_eq!(
3217 relation.version(),
3218 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap()))
3219 );
3220 assert_eq!(
3221 relation.architectures().unwrap().collect::<Vec<_>>(),
3222 vec!["i386", "arm"]
3223 .into_iter()
3224 .map(|s| s.to_string())
3225 .collect::<Vec<_>>()
3226 );
3227 assert_eq!(
3228 relation.profiles().collect::<Vec<_>>(),
3229 vec![
3230 vec![BuildProfile::Disabled("nocheck".to_string())],
3231 vec![BuildProfile::Disabled("cross".to_string())]
3232 ]
3233 );
3234 }
3235
3236 #[test]
3237 fn test_substvar() {
3238 let input = "${shlibs:Depends}";
3239
3240 let (parsed, errors) = Relations::parse_relaxed(input, true);
3241 assert_eq!(errors, Vec::<String>::new());
3242 assert_eq!(parsed.to_string(), input);
3243 assert_eq!(parsed.entries().count(), 0);
3244
3245 assert_eq!(
3246 parsed.substvars().collect::<Vec<_>>(),
3247 vec!["${shlibs:Depends}"]
3248 );
3249 }
3250
3251 #[test]
3252 fn test_new() {
3253 let r = Relation::new(
3254 "samba",
3255 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
3256 );
3257
3258 assert_eq!(r.to_string(), "samba (>= 2.0)");
3259 }
3260
3261 #[test]
3262 fn test_drop_constraint() {
3263 let mut r = Relation::new(
3264 "samba",
3265 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
3266 );
3267
3268 r.drop_constraint();
3269
3270 assert_eq!(r.to_string(), "samba");
3271 }
3272
3273 #[test]
3274 fn test_simple() {
3275 let r = Relation::simple("samba");
3276
3277 assert_eq!(r.to_string(), "samba");
3278 }
3279
3280 #[test]
3281 fn test_remove_first_entry() {
3282 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3283 .parse()
3284 .unwrap();
3285 let removed = rels.remove_entry(0);
3286 assert_eq!(removed.to_string(), "python3-dulwich (>= 0.20.21)");
3287 assert_eq!(rels.to_string(), "python3-dulwich (<< 0.21)");
3288 }
3289
3290 #[test]
3291 fn test_remove_last_entry() {
3292 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3293 .parse()
3294 .unwrap();
3295 rels.remove_entry(1);
3296 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
3297 }
3298
3299 #[test]
3300 fn test_remove_middle() {
3301 let mut rels: Relations =
3302 r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21), python3-dulwich (<< 0.22)"#
3303 .parse()
3304 .unwrap();
3305 rels.remove_entry(1);
3306 assert_eq!(
3307 rels.to_string(),
3308 "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.22)"
3309 );
3310 }
3311
3312 #[test]
3313 fn test_remove_added() {
3314 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
3315 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3316 rels.push(entry);
3317 rels.remove_entry(1);
3318 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
3319 }
3320
3321 #[test]
3322 fn test_push() {
3323 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
3324 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3325 rels.push(entry);
3326 assert_eq!(
3327 rels.to_string(),
3328 "python3-dulwich (>= 0.20.21), python3-dulwich"
3329 );
3330 }
3331
3332 #[test]
3333 fn test_insert_with_custom_separator() {
3334 let mut rels: Relations = "python3".parse().unwrap();
3335 let entry = Entry::from(vec![Relation::simple("debhelper")]);
3336 rels.insert_with_separator(1, entry, Some("\n "));
3337 assert_eq!(rels.to_string(), "python3,\n debhelper");
3338 }
3339
3340 #[test]
3341 fn test_insert_with_wrap_and_sort_separator() {
3342 let mut rels: Relations = "python3".parse().unwrap();
3343 let entry = Entry::from(vec![Relation::simple("rustc")]);
3344 rels.insert_with_separator(1, entry, Some("\n "));
3346 assert_eq!(rels.to_string(), "python3,\n rustc");
3347 }
3348
3349 #[test]
3350 fn test_push_from_empty() {
3351 let mut rels: Relations = "".parse().unwrap();
3352 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3353 rels.push(entry);
3354 assert_eq!(rels.to_string(), "python3-dulwich");
3355 }
3356
3357 #[test]
3358 fn test_insert() {
3359 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3360 .parse()
3361 .unwrap();
3362 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3363 rels.insert(1, entry);
3364 assert_eq!(
3365 rels.to_string(),
3366 "python3-dulwich (>= 0.20.21), python3-dulwich, python3-dulwich (<< 0.21)"
3367 );
3368 }
3369
3370 #[test]
3371 fn test_insert_at_start() {
3372 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3373 .parse()
3374 .unwrap();
3375 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3376 rels.insert(0, entry);
3377 assert_eq!(
3378 rels.to_string(),
3379 "python3-dulwich, python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3380 );
3381 }
3382
3383 #[test]
3384 fn test_insert_after_error() {
3385 let (mut rels, errors) = Relations::parse_relaxed("@foo@, debhelper (>= 1.0)", false);
3386 assert_eq!(
3387 errors,
3388 vec![
3389 "expected $ or identifier but got ERROR",
3390 "expected comma or end of file but got Some(IDENT)",
3391 "expected $ or identifier but got ERROR"
3392 ]
3393 );
3394 let entry = Entry::from(vec![Relation::simple("bar")]);
3395 rels.push(entry);
3396 assert_eq!(rels.to_string(), "@foo@, debhelper (>= 1.0), bar");
3397 }
3398
3399 #[test]
3400 fn test_insert_before_error() {
3401 let (mut rels, errors) = Relations::parse_relaxed("debhelper (>= 1.0), @foo@, bla", false);
3402 assert_eq!(
3403 errors,
3404 vec![
3405 "expected $ or identifier but got ERROR",
3406 "expected comma or end of file but got Some(IDENT)",
3407 "expected $ or identifier but got ERROR"
3408 ]
3409 );
3410 let entry = Entry::from(vec![Relation::simple("bar")]);
3411 rels.insert(0, entry);
3412 assert_eq!(rels.to_string(), "bar, debhelper (>= 1.0), @foo@, bla");
3413 }
3414
3415 #[test]
3416 fn test_replace() {
3417 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3418 .parse()
3419 .unwrap();
3420 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3421 rels.replace(1, entry);
3422 assert_eq!(
3423 rels.to_string(),
3424 "python3-dulwich (>= 0.20.21), python3-dulwich"
3425 );
3426 }
3427
3428 #[test]
3429 fn test_relation_from_entries() {
3430 let entries = vec![
3431 Entry::from(vec![Relation::simple("python3-dulwich")]),
3432 Entry::from(vec![Relation::simple("python3-breezy")]),
3433 ];
3434 let rels: Relations = entries.into();
3435 assert_eq!(rels.entries().count(), 2);
3436 assert_eq!(rels.to_string(), "python3-dulwich, python3-breezy");
3437 }
3438
3439 #[test]
3440 fn test_entry_from_relations() {
3441 let relations = vec![
3442 Relation::simple("python3-dulwich"),
3443 Relation::simple("python3-breezy"),
3444 ];
3445 let entry: Entry = relations.into();
3446 assert_eq!(entry.relations().count(), 2);
3447 assert_eq!(entry.to_string(), "python3-dulwich | python3-breezy");
3448 }
3449
3450 #[test]
3451 fn test_parse_entry() {
3452 let parsed: Entry = "python3-dulwich (>= 0.20.21) | bar".parse().unwrap();
3453 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21) | bar");
3454 assert_eq!(parsed.relations().count(), 2);
3455
3456 assert_eq!(
3457 "foo, bar".parse::<Entry>().unwrap_err(),
3458 "Multiple entries found"
3459 );
3460 assert_eq!("".parse::<Entry>().unwrap_err(), "No entry found");
3461 }
3462
3463 #[test]
3464 fn test_parse_relation() {
3465 let parsed: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3466 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21)");
3467 assert_eq!(
3468 parsed.version(),
3469 Some((
3470 VersionConstraint::GreaterThanEqual,
3471 "0.20.21".parse().unwrap()
3472 ))
3473 );
3474 assert_eq!(
3475 "foo | bar".parse::<Relation>().unwrap_err(),
3476 "Multiple relations found"
3477 );
3478 assert_eq!("".parse::<Relation>().unwrap_err(), "No entry found");
3479 }
3480
3481 #[test]
3482 fn test_special() {
3483 let parsed: Relation = "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
3484 .parse()
3485 .unwrap();
3486 assert_eq!(
3487 parsed.to_string(),
3488 "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
3489 );
3490 assert_eq!(
3491 parsed.version(),
3492 Some((
3493 VersionConstraint::GreaterThanEqual,
3494 "0.1.138-~~".parse().unwrap()
3495 ))
3496 );
3497 assert_eq!(parsed.archqual(), Some("amd64".to_string()));
3498 assert_eq!(parsed.name(), "librust-breezyshim+dirty-tracker-dev");
3499 }
3500
3501 #[test]
3502 fn test_relations_satisfied_by() {
3503 let rels: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3504 .parse()
3505 .unwrap();
3506 let satisfied = |name: &str| -> Option<debversion::Version> {
3507 match name {
3508 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
3509 _ => None,
3510 }
3511 };
3512 assert!(rels.satisfied_by(satisfied));
3513
3514 let satisfied = |name: &str| match name {
3515 "python3-dulwich" => Some("0.21".parse().unwrap()),
3516 _ => None,
3517 };
3518 assert!(!rels.satisfied_by(satisfied));
3519
3520 let satisfied = |name: &str| match name {
3521 "python3-dulwich" => Some("0.20.20".parse().unwrap()),
3522 _ => None,
3523 };
3524 assert!(!rels.satisfied_by(satisfied));
3525 }
3526
3527 #[test]
3528 fn test_entry_satisfied_by() {
3529 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3530 .parse()
3531 .unwrap();
3532 let satisfied = |name: &str| -> Option<debversion::Version> {
3533 match name {
3534 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
3535 _ => None,
3536 }
3537 };
3538 assert!(entry.satisfied_by(satisfied));
3539 let satisfied = |name: &str| -> Option<debversion::Version> {
3540 match name {
3541 "python3-dulwich" => Some("0.18".parse().unwrap()),
3542 _ => None,
3543 }
3544 };
3545 assert!(!entry.satisfied_by(satisfied));
3546 }
3547
3548 #[test]
3549 fn test_wrap_and_sort_relation() {
3550 let relation: Relation = " python3-dulwich (>= 11) [ amd64 ] < lala>"
3551 .parse()
3552 .unwrap();
3553
3554 let wrapped = relation.wrap_and_sort();
3555
3556 assert_eq!(
3557 wrapped.to_string(),
3558 "python3-dulwich (>= 11) [amd64] <lala>"
3559 );
3560 }
3561
3562 #[test]
3563 fn test_wrap_and_sort_relations() {
3564 let entry: Relations =
3565 "python3-dulwich (>= 0.20.21) | bar, \n\n\n\npython3-dulwich (<< 0.21)"
3566 .parse()
3567 .unwrap();
3568
3569 let wrapped = entry.wrap_and_sort();
3570
3571 assert_eq!(
3572 wrapped.to_string(),
3573 "bar | python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3574 );
3575 }
3576
3577 #[cfg(feature = "serde")]
3578 #[test]
3579 fn test_serialize_relations() {
3580 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3581 .parse()
3582 .unwrap();
3583 let serialized = serde_json::to_string(&relations).unwrap();
3584 assert_eq!(
3585 serialized,
3586 r#""python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)""#
3587 );
3588 }
3589
3590 #[cfg(feature = "serde")]
3591 #[test]
3592 fn test_deserialize_relations() {
3593 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3594 .parse()
3595 .unwrap();
3596 let serialized = serde_json::to_string(&relations).unwrap();
3597 let deserialized: Relations = serde_json::from_str(&serialized).unwrap();
3598 assert_eq!(deserialized.to_string(), relations.to_string());
3599 }
3600
3601 #[cfg(feature = "serde")]
3602 #[test]
3603 fn test_serialize_relation() {
3604 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3605 let serialized = serde_json::to_string(&relation).unwrap();
3606 assert_eq!(serialized, r#""python3-dulwich (>= 0.20.21)""#);
3607 }
3608
3609 #[cfg(feature = "serde")]
3610 #[test]
3611 fn test_deserialize_relation() {
3612 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3613 let serialized = serde_json::to_string(&relation).unwrap();
3614 let deserialized: Relation = serde_json::from_str(&serialized).unwrap();
3615 assert_eq!(deserialized.to_string(), relation.to_string());
3616 }
3617
3618 #[cfg(feature = "serde")]
3619 #[test]
3620 fn test_serialize_entry() {
3621 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3622 .parse()
3623 .unwrap();
3624 let serialized = serde_json::to_string(&entry).unwrap();
3625 assert_eq!(
3626 serialized,
3627 r#""python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)""#
3628 );
3629 }
3630
3631 #[cfg(feature = "serde")]
3632 #[test]
3633 fn test_deserialize_entry() {
3634 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3635 .parse()
3636 .unwrap();
3637 let serialized = serde_json::to_string(&entry).unwrap();
3638 let deserialized: Entry = serde_json::from_str(&serialized).unwrap();
3639 assert_eq!(deserialized.to_string(), entry.to_string());
3640 }
3641
3642 #[test]
3643 fn test_remove_first_relation() {
3644 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3645 .parse()
3646 .unwrap();
3647 let mut rel = entry.relations().next().unwrap();
3648 rel.remove();
3649 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.18)");
3650 }
3651
3652 #[test]
3653 fn test_remove_last_relation() {
3654 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3655 .parse()
3656 .unwrap();
3657 let mut rel = entry.relations().nth(1).unwrap();
3658 rel.remove();
3659 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3660 }
3661
3662 #[test]
3663 fn test_remove_only_relation() {
3664 let entry: Entry = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3665 let mut rel = entry.relations().next().unwrap();
3666 rel.remove();
3667 assert_eq!(entry.to_string(), "");
3668 }
3669
3670 #[test]
3671 fn test_relations_is_empty() {
3672 let entry: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3673 assert!(!entry.is_empty());
3674 assert_eq!(1, entry.len());
3675 let mut rel = entry.entries().next().unwrap();
3676 rel.remove();
3677 assert!(entry.is_empty());
3678 assert_eq!(0, entry.len());
3679 }
3680
3681 #[test]
3682 fn test_entry_is_empty() {
3683 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3684 .parse()
3685 .unwrap();
3686 assert!(!entry.is_empty());
3687 assert_eq!(2, entry.len());
3688 let mut rel = entry.relations().next().unwrap();
3689 rel.remove();
3690 assert!(!entry.is_empty());
3691 assert_eq!(1, entry.len());
3692 let mut rel = entry.relations().next().unwrap();
3693 rel.remove();
3694 assert!(entry.is_empty());
3695 assert_eq!(0, entry.len());
3696 }
3697
3698 #[test]
3699 fn test_relation_set_version() {
3700 let mut rel: Relation = "samba".parse().unwrap();
3701 rel.set_version(None);
3702 assert_eq!("samba", rel.to_string());
3703 rel.set_version(Some((
3704 VersionConstraint::GreaterThanEqual,
3705 "2.0".parse().unwrap(),
3706 )));
3707 assert_eq!("samba (>= 2.0)", rel.to_string());
3708 rel.set_version(None);
3709 assert_eq!("samba", rel.to_string());
3710 rel.set_version(Some((
3711 VersionConstraint::GreaterThanEqual,
3712 "2.0".parse().unwrap(),
3713 )));
3714 rel.set_version(Some((
3715 VersionConstraint::GreaterThanEqual,
3716 "1.1".parse().unwrap(),
3717 )));
3718 assert_eq!("samba (>= 1.1)", rel.to_string());
3719 }
3720
3721 #[test]
3722 fn test_replace_relation() {
3723 let mut entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3724 .parse()
3725 .unwrap();
3726 let new_rel = Relation::simple("python3-breezy");
3727 entry.replace(0, new_rel);
3728 assert_eq!(
3729 entry.to_string(),
3730 "python3-breezy | python3-dulwich (<< 0.18)"
3731 );
3732 }
3733
3734 #[test]
3735 fn test_entry_push_relation() {
3736 let relations: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3737 let new_rel = Relation::simple("python3-breezy");
3738 let mut entry = relations.entries().next().unwrap();
3739 entry.push(new_rel);
3740 assert_eq!(
3741 entry.to_string(),
3742 "python3-dulwich (>= 0.20.21) | python3-breezy"
3743 );
3744 assert_eq!(
3745 relations.to_string(),
3746 "python3-dulwich (>= 0.20.21) | python3-breezy"
3747 );
3748 }
3749
3750 #[test]
3751 fn test_relations_remove_empty_entry() {
3752 let (mut relations, errors) = Relations::parse_relaxed("foo, , bar, ", false);
3753 assert_eq!(errors, Vec::<String>::new());
3754 assert_eq!(relations.to_string(), "foo, , bar, ");
3755 assert_eq!(relations.len(), 2);
3756 assert_eq!(
3757 relations.entries().next().unwrap().to_string(),
3758 "foo".to_string()
3759 );
3760 assert_eq!(
3761 relations.entries().nth(1).unwrap().to_string(),
3762 "bar".to_string()
3763 );
3764 relations.remove_entry(1);
3765 assert_eq!(relations.to_string(), "foo, , ");
3766 }
3767
3768 #[test]
3769 fn test_entry_remove_relation() {
3770 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
3771 let removed = entry.remove_relation(0);
3772 assert_eq!(removed.to_string(), "python3-dulwich");
3773 assert_eq!(entry.to_string(), "samba");
3774 }
3775
3776 #[test]
3777 fn test_wrap_and_sort_removes_empty_entries() {
3778 let relations: Relations = "foo, , bar, ".parse().unwrap();
3779 let wrapped = relations.wrap_and_sort();
3780 assert_eq!(wrapped.to_string(), "bar, foo");
3781 }
3782
3783 #[test]
3784 fn test_set_archqual() {
3785 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
3786 let mut rel = entry.relations().next().unwrap();
3787 rel.set_archqual("amd64");
3788 assert_eq!(rel.to_string(), "python3-dulwich:amd64");
3789 assert_eq!(rel.archqual(), Some("amd64".to_string()));
3790 assert_eq!(entry.to_string(), "python3-dulwich:amd64 | samba");
3791 rel.set_archqual("i386");
3792 assert_eq!(rel.to_string(), "python3-dulwich:i386");
3793 assert_eq!(rel.archqual(), Some("i386".to_string()));
3794 assert_eq!(entry.to_string(), "python3-dulwich:i386 | samba");
3795 }
3796
3797 #[test]
3798 fn test_set_architectures() {
3799 let mut relation = Relation::simple("samba");
3800 relation.set_architectures(vec!["amd64", "i386"].into_iter());
3801 assert_eq!(relation.to_string(), "samba [amd64 i386]");
3802 }
3803
3804 #[test]
3805 fn test_relation_builder_no_architectures() {
3806 let relation = Relation::build("debhelper").build();
3808 assert_eq!(relation.to_string(), "debhelper");
3809 }
3810
3811 #[test]
3812 fn test_relation_builder_with_architectures() {
3813 let relation = Relation::build("samba")
3815 .architectures(vec!["amd64".to_string(), "i386".to_string()])
3816 .build();
3817 assert_eq!(relation.to_string(), "samba [amd64 i386]");
3818 }
3819
3820 #[test]
3821 fn test_ensure_minimum_version_add_new() {
3822 let mut relations: Relations = "python3".parse().unwrap();
3823 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3824 assert_eq!(relations.to_string(), "python3, debhelper (>= 12)");
3825 }
3826
3827 #[test]
3828 fn test_ensure_minimum_version_update() {
3829 let mut relations: Relations = "debhelper (>= 9)".parse().unwrap();
3830 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3831 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3832 }
3833
3834 #[test]
3835 fn test_ensure_minimum_version_no_change() {
3836 let mut relations: Relations = "debhelper (>= 13)".parse().unwrap();
3837 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3838 assert_eq!(relations.to_string(), "debhelper (>= 13)");
3839 }
3840
3841 #[test]
3842 fn test_ensure_minimum_version_no_version() {
3843 let mut relations: Relations = "debhelper".parse().unwrap();
3844 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3845 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3846 }
3847
3848 #[test]
3849 fn test_ensure_minimum_version_preserves_newline() {
3850 let input = "\n debhelper (>= 9),\n pkg-config,\n uuid-dev";
3856 let mut relations: Relations = input.parse().unwrap();
3857 relations.ensure_minimum_version("debhelper", &"12~".parse().unwrap());
3858 let result = relations.to_string();
3859
3860 assert!(
3862 result.starts_with('\n'),
3863 "Expected result to start with newline, got: {:?}",
3864 result
3865 );
3866 assert_eq!(result, "\n debhelper (>= 12~),\n pkg-config,\n uuid-dev");
3867 }
3868
3869 #[test]
3870 fn test_ensure_minimum_version_preserves_newline_in_control() {
3871 use crate::lossless::Control;
3873 use std::str::FromStr;
3874
3875 let input = r#"Source: f2fs-tools
3876Section: admin
3877Priority: optional
3878Maintainer: Test <test@example.com>
3879Build-Depends:
3880 debhelper (>= 9),
3881 pkg-config,
3882 uuid-dev
3883
3884Package: f2fs-tools
3885Description: test
3886"#;
3887
3888 let control = Control::from_str(input).unwrap();
3889 let mut source = control.source().unwrap();
3890 let mut build_depends = source.build_depends().unwrap();
3891
3892 let version = Version::from_str("12~").unwrap();
3893 build_depends.ensure_minimum_version("debhelper", &version);
3894
3895 source.set_build_depends(&build_depends);
3896
3897 let output = control.to_string();
3898
3899 assert!(
3901 output.contains("Build-Depends:\n debhelper (>= 12~)"),
3902 "Expected 'Build-Depends:\\n debhelper (>= 12~)' but got:\n{}",
3903 output
3904 );
3905 }
3906
3907 #[test]
3908 fn test_ensure_exact_version_add_new() {
3909 let mut relations: Relations = "python3".parse().unwrap();
3910 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3911 assert_eq!(relations.to_string(), "python3, debhelper (= 12)");
3912 }
3913
3914 #[test]
3915 fn test_ensure_exact_version_update() {
3916 let mut relations: Relations = "debhelper (>= 9)".parse().unwrap();
3917 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3918 assert_eq!(relations.to_string(), "debhelper (= 12)");
3919 }
3920
3921 #[test]
3922 fn test_ensure_exact_version_no_change() {
3923 let mut relations: Relations = "debhelper (= 12)".parse().unwrap();
3924 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3925 assert_eq!(relations.to_string(), "debhelper (= 12)");
3926 }
3927
3928 #[test]
3929 fn test_ensure_some_version_add_new() {
3930 let mut relations: Relations = "python3".parse().unwrap();
3931 relations.ensure_some_version("debhelper");
3932 assert_eq!(relations.to_string(), "python3, debhelper");
3933 }
3934
3935 #[test]
3936 fn test_ensure_some_version_exists_with_version() {
3937 let mut relations: Relations = "debhelper (>= 12)".parse().unwrap();
3938 relations.ensure_some_version("debhelper");
3939 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3940 }
3941
3942 #[test]
3943 fn test_ensure_some_version_exists_no_version() {
3944 let mut relations: Relations = "debhelper".parse().unwrap();
3945 relations.ensure_some_version("debhelper");
3946 assert_eq!(relations.to_string(), "debhelper");
3947 }
3948
3949 #[test]
3950 fn test_ensure_substvar() {
3951 let mut relations: Relations = "python3".parse().unwrap();
3952 relations.ensure_substvar("${misc:Depends}").unwrap();
3953 assert_eq!(relations.to_string(), "python3, ${misc:Depends}");
3954 }
3955
3956 #[test]
3957 fn test_ensure_substvar_already_exists() {
3958 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
3959 relations.ensure_substvar("${misc:Depends}").unwrap();
3960 assert_eq!(relations.to_string(), "python3, ${misc:Depends}");
3961 }
3962
3963 #[test]
3964 fn test_ensure_substvar_empty_relations() {
3965 let mut relations: Relations = Relations::new();
3966 relations.ensure_substvar("${misc:Depends}").unwrap();
3967 assert_eq!(relations.to_string(), "${misc:Depends}");
3968 }
3969
3970 #[test]
3971 fn test_ensure_substvar_preserves_whitespace() {
3972 let (mut relations, _) = Relations::parse_relaxed("python3, rustc", false);
3974 relations.ensure_substvar("${misc:Depends}").unwrap();
3975 assert_eq!(relations.to_string(), "python3, rustc, ${misc:Depends}");
3977 }
3978
3979 #[test]
3980 fn test_ensure_substvar_to_existing_substvar() {
3981 let (mut relations, _) = Relations::parse_relaxed("${shlibs:Depends}", true);
3984 relations.ensure_substvar("${misc:Depends}").unwrap();
3985 assert_eq!(relations.to_string(), "${shlibs:Depends}, ${misc:Depends}");
3987 }
3988
3989 #[test]
3990 fn test_drop_substvar_basic() {
3991 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
3992 relations.drop_substvar("${misc:Depends}");
3993 assert_eq!(relations.to_string(), "python3");
3994 }
3995
3996 #[test]
3997 fn test_drop_substvar_first_position() {
3998 let (mut relations, _) = Relations::parse_relaxed("${misc:Depends}, python3", true);
3999 relations.drop_substvar("${misc:Depends}");
4000 assert_eq!(relations.to_string(), "python3");
4001 }
4002
4003 #[test]
4004 fn test_drop_substvar_middle_position() {
4005 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}, rustc", true);
4006 relations.drop_substvar("${misc:Depends}");
4007 assert_eq!(relations.to_string(), "python3, rustc");
4008 }
4009
4010 #[test]
4011 fn test_drop_substvar_only_substvar() {
4012 let (mut relations, _) = Relations::parse_relaxed("${misc:Depends}", true);
4013 relations.drop_substvar("${misc:Depends}");
4014 assert_eq!(relations.to_string(), "");
4015 }
4016
4017 #[test]
4018 fn test_drop_substvar_not_exists() {
4019 let (mut relations, _) = Relations::parse_relaxed("python3, rustc", false);
4020 relations.drop_substvar("${misc:Depends}");
4021 assert_eq!(relations.to_string(), "python3, rustc");
4022 }
4023
4024 #[test]
4025 fn test_drop_substvar_multiple_substvars() {
4026 let (mut relations, _) =
4027 Relations::parse_relaxed("python3, ${misc:Depends}, ${shlibs:Depends}", true);
4028 relations.drop_substvar("${misc:Depends}");
4029 assert_eq!(relations.to_string(), "python3, ${shlibs:Depends}");
4030 }
4031
4032 #[test]
4033 fn test_drop_substvar_preserves_whitespace() {
4034 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
4035 relations.drop_substvar("${misc:Depends}");
4036 assert_eq!(relations.to_string(), "python3");
4037 }
4038
4039 #[test]
4040 fn test_filter_entries_basic() {
4041 let mut relations: Relations = "python3, debhelper, rustc".parse().unwrap();
4042 relations.filter_entries(|entry| entry.relations().any(|r| r.name().starts_with("python")));
4043 assert_eq!(relations.to_string(), "python3");
4044 }
4045
4046 #[test]
4047 fn test_filter_entries_keep_all() {
4048 let mut relations: Relations = "python3, debhelper".parse().unwrap();
4049 relations.filter_entries(|_| true);
4050 assert_eq!(relations.to_string(), "python3, debhelper");
4051 }
4052
4053 #[test]
4054 fn test_filter_entries_remove_all() {
4055 let mut relations: Relations = "python3, debhelper".parse().unwrap();
4056 relations.filter_entries(|_| false);
4057 assert_eq!(relations.to_string(), "");
4058 }
4059
4060 #[test]
4061 fn test_filter_entries_keep_middle() {
4062 let mut relations: Relations = "aaa, bbb, ccc".parse().unwrap();
4063 relations.filter_entries(|entry| entry.relations().any(|r| r.name() == "bbb"));
4064 assert_eq!(relations.to_string(), "bbb");
4065 }
4066
4067 #[test]
4070 fn test_is_sorted_wrap_and_sort_order() {
4071 let relations: Relations = "debhelper, python3, rustc".parse().unwrap();
4073 assert!(relations.is_sorted(&WrapAndSortOrder));
4074
4075 let relations: Relations = "rustc, debhelper, python3".parse().unwrap();
4077 assert!(!relations.is_sorted(&WrapAndSortOrder));
4078
4079 let (relations, _) =
4081 Relations::parse_relaxed("cdbs, debhelper-compat, python3, ${misc:Depends}", true);
4082 assert!(relations.is_sorted(&WrapAndSortOrder));
4083 }
4084
4085 #[test]
4086 fn test_is_sorted_default_order() {
4087 let relations: Relations = "aaa, bbb, ccc".parse().unwrap();
4089 assert!(relations.is_sorted(&DefaultSortingOrder));
4090
4091 let relations: Relations = "ccc, aaa, bbb".parse().unwrap();
4093 assert!(!relations.is_sorted(&DefaultSortingOrder));
4094
4095 let (relations, _) = Relations::parse_relaxed("aaa, bbb, ${misc:Depends}", true);
4097 assert!(relations.is_sorted(&DefaultSortingOrder));
4098 }
4099
4100 #[test]
4101 fn test_is_sorted_with_substvars() {
4102 let (relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}, rustc", true);
4104 assert!(relations.is_sorted(&DefaultSortingOrder));
4106 }
4107
4108 #[test]
4109 fn test_drop_dependency_exists() {
4110 let mut relations: Relations = "python3, debhelper, rustc".parse().unwrap();
4111 assert!(relations.drop_dependency("debhelper"));
4112 assert_eq!(relations.to_string(), "python3, rustc");
4113 }
4114
4115 #[test]
4116 fn test_drop_dependency_not_exists() {
4117 let mut relations: Relations = "python3, rustc".parse().unwrap();
4118 assert!(!relations.drop_dependency("nonexistent"));
4119 assert_eq!(relations.to_string(), "python3, rustc");
4120 }
4121
4122 #[test]
4123 fn test_drop_dependency_only_item() {
4124 let mut relations: Relations = "python3".parse().unwrap();
4125 assert!(relations.drop_dependency("python3"));
4126 assert_eq!(relations.to_string(), "");
4127 }
4128
4129 #[test]
4130 fn test_add_dependency_to_empty() {
4131 let mut relations: Relations = "".parse().unwrap();
4132 let entry = Entry::from(Relation::simple("python3"));
4133 relations.add_dependency(entry, None);
4134 assert_eq!(relations.to_string(), "python3");
4135 }
4136
4137 #[test]
4138 fn test_add_dependency_sorted_position() {
4139 let mut relations: Relations = "debhelper, rustc".parse().unwrap();
4140 let entry = Entry::from(Relation::simple("python3"));
4141 relations.add_dependency(entry, None);
4142 assert_eq!(relations.to_string(), "debhelper, python3, rustc");
4144 }
4145
4146 #[test]
4147 fn test_add_dependency_explicit_position() {
4148 let mut relations: Relations = "python3, rustc".parse().unwrap();
4149 let entry = Entry::from(Relation::simple("debhelper"));
4150 relations.add_dependency(entry, Some(0));
4151 assert_eq!(relations.to_string(), "debhelper, python3, rustc");
4152 }
4153
4154 #[test]
4155 fn test_add_dependency_build_system_first() {
4156 let mut relations: Relations = "python3, rustc".parse().unwrap();
4157 let entry = Entry::from(Relation::simple("debhelper-compat"));
4158 relations.add_dependency(entry, None);
4159 assert_eq!(relations.to_string(), "debhelper-compat, python3, rustc");
4161 }
4162
4163 #[test]
4164 fn test_add_dependency_at_end() {
4165 let mut relations: Relations = "debhelper, python3".parse().unwrap();
4166 let entry = Entry::from(Relation::simple("zzz-package"));
4167 relations.add_dependency(entry, None);
4168 assert_eq!(relations.to_string(), "debhelper, python3, zzz-package");
4170 }
4171
4172 #[test]
4173 fn test_add_dependency_to_single_entry() {
4174 let mut relations: Relations = "python3-dulwich".parse().unwrap();
4176 let entry: Entry = "debhelper-compat (= 12)".parse().unwrap();
4177 relations.add_dependency(entry, None);
4178 assert_eq!(
4180 relations.to_string(),
4181 "debhelper-compat (= 12), python3-dulwich"
4182 );
4183 }
4184
4185 #[test]
4186 fn test_get_relation_exists() {
4187 let relations: Relations = "python3, debhelper (>= 12), rustc".parse().unwrap();
4188 let result = relations.get_relation("debhelper");
4189 assert!(result.is_ok());
4190 let (idx, entry) = result.unwrap();
4191 assert_eq!(idx, 1);
4192 assert_eq!(entry.to_string(), "debhelper (>= 12)");
4193 }
4194
4195 #[test]
4196 fn test_get_relation_not_exists() {
4197 let relations: Relations = "python3, rustc".parse().unwrap();
4198 let result = relations.get_relation("nonexistent");
4199 assert_eq!(result, Err("Package nonexistent not found".to_string()));
4200 }
4201
4202 #[test]
4203 fn test_get_relation_complex_rule() {
4204 let relations: Relations = "python3 | python3-minimal, rustc".parse().unwrap();
4205 let result = relations.get_relation("python3");
4206 assert_eq!(
4207 result,
4208 Err("Complex rule for python3, aborting".to_string())
4209 );
4210 }
4211
4212 #[test]
4213 fn test_iter_relations_for_simple() {
4214 let relations: Relations = "python3, debhelper, python3-dev".parse().unwrap();
4215 let entries: Vec<_> = relations.iter_relations_for("python3").collect();
4216 assert_eq!(entries.len(), 1);
4217 assert_eq!(entries[0].0, 0);
4218 assert_eq!(entries[0].1.to_string(), "python3");
4219 }
4220
4221 #[test]
4222 fn test_iter_relations_for_alternatives() {
4223 let relations: Relations = "python3 | python3-minimal, python3-dev".parse().unwrap();
4224 let entries: Vec<_> = relations.iter_relations_for("python3").collect();
4225 assert_eq!(entries.len(), 1);
4227 assert_eq!(entries[0].0, 0);
4228 }
4229
4230 #[test]
4231 fn test_iter_relations_for_not_found() {
4232 let relations: Relations = "python3, rustc".parse().unwrap();
4233 let entries: Vec<_> = relations.iter_relations_for("debhelper").collect();
4234 assert_eq!(entries.len(), 0);
4235 }
4236
4237 #[test]
4238 fn test_has_relation_exists() {
4239 let relations: Relations = "python3, debhelper, rustc".parse().unwrap();
4240 assert!(relations.has_relation("debhelper"));
4241 assert!(relations.has_relation("python3"));
4242 assert!(relations.has_relation("rustc"));
4243 }
4244
4245 #[test]
4246 fn test_has_relation_not_exists() {
4247 let relations: Relations = "python3, rustc".parse().unwrap();
4248 assert!(!relations.has_relation("debhelper"));
4249 }
4250
4251 #[test]
4252 fn test_has_relation_in_alternative() {
4253 let relations: Relations = "python3 | python3-minimal".parse().unwrap();
4254 assert!(relations.has_relation("python3"));
4255 assert!(relations.has_relation("python3-minimal"));
4256 }
4257
4258 #[test]
4259 fn test_sorting_order_wrap_and_sort_build_systems() {
4260 let order = WrapAndSortOrder;
4261 assert!(order.lt("debhelper", "python3"));
4263 assert!(order.lt("debhelper-compat", "rustc"));
4264 assert!(order.lt("cdbs", "aaa"));
4265 assert!(order.lt("dh-python", "python3"));
4266 }
4267
4268 #[test]
4269 fn test_sorting_order_wrap_and_sort_regular_packages() {
4270 let order = WrapAndSortOrder;
4271 assert!(order.lt("aaa", "bbb"));
4273 assert!(order.lt("python3", "rustc"));
4274 assert!(!order.lt("rustc", "python3"));
4275 }
4276
4277 #[test]
4278 fn test_sorting_order_wrap_and_sort_substvars() {
4279 let order = WrapAndSortOrder;
4280 assert!(order.lt("python3", "${misc:Depends}"));
4282 assert!(!order.lt("${misc:Depends}", "python3"));
4283 assert!(!order.ignore("${misc:Depends}"));
4285 }
4286
4287 #[test]
4288 fn test_sorting_order_default_special_items() {
4289 let order = DefaultSortingOrder;
4290 assert!(order.lt("python3", "${misc:Depends}"));
4292 assert!(order.lt("aaa", "@cdbs@"));
4293 assert!(order.ignore("${misc:Depends}"));
4295 assert!(order.ignore("@cdbs@"));
4296 assert!(!order.ignore("python3"));
4297 }
4298
4299 #[test]
4300 fn test_is_special_package_name() {
4301 assert!(is_special_package_name("${misc:Depends}"));
4302 assert!(is_special_package_name("${shlibs:Depends}"));
4303 assert!(is_special_package_name("@cdbs@"));
4304 assert!(!is_special_package_name("python3"));
4305 assert!(!is_special_package_name("debhelper"));
4306 }
4307
4308 #[test]
4309 fn test_add_dependency_with_explicit_position() {
4310 let mut relations: Relations = "python3, rustc".parse().unwrap();
4312 let entry = Entry::from(Relation::simple("debhelper"));
4313 relations.add_dependency(entry, Some(1));
4314 assert_eq!(relations.to_string(), "python3, debhelper, rustc");
4316 }
4317
4318 #[test]
4319 fn test_whitespace_detection_single_space() {
4320 let mut relations: Relations = "python3, rustc".parse().unwrap();
4321 let entry = Entry::from(Relation::simple("debhelper"));
4322 relations.add_dependency(entry, Some(1));
4323 assert_eq!(relations.to_string(), "python3, debhelper, rustc");
4324 }
4325
4326 #[test]
4327 fn test_whitespace_detection_multiple_spaces() {
4328 let mut relations: Relations = "python3, rustc, gcc".parse().unwrap();
4329 let entry = Entry::from(Relation::simple("debhelper"));
4330 relations.add_dependency(entry, Some(1));
4331 assert_eq!(relations.to_string(), "python3, debhelper, rustc, gcc");
4333 }
4334
4335 #[test]
4336 fn test_whitespace_detection_mixed_patterns() {
4337 let mut relations: Relations = "a, b, c, d, e".parse().unwrap();
4339 let entry = Entry::from(Relation::simple("x"));
4340 relations.push(entry);
4341 assert_eq!(relations.to_string(), "a, b, c, d, e, x");
4344 }
4345
4346 #[test]
4347 fn test_whitespace_detection_newlines() {
4348 let mut relations: Relations = "python3,\n rustc".parse().unwrap();
4349 let entry = Entry::from(Relation::simple("debhelper"));
4350 relations.add_dependency(entry, Some(1));
4351 assert_eq!(relations.to_string(), "python3,\n debhelper,\n rustc");
4353 }
4354
4355 #[test]
4356 fn test_append_with_newline_no_trailing() {
4357 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4358 let entry = Entry::from(Relation::simple("blah"));
4359 relations.add_dependency(entry, None);
4360 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4361 }
4362
4363 #[test]
4364 fn test_append_with_trailing_newline() {
4365 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4366 let entry = Entry::from(Relation::simple("blah"));
4367 relations.add_dependency(entry, None);
4368 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4369 }
4370
4371 #[test]
4372 fn test_append_with_4_space_indent() {
4373 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4374 let entry = Entry::from(Relation::simple("blah"));
4375 relations.add_dependency(entry, None);
4376 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4377 }
4378
4379 #[test]
4380 fn test_append_with_4_space_and_trailing_newline() {
4381 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4382 let entry = Entry::from(Relation::simple("blah"));
4383 relations.add_dependency(entry, None);
4384 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4385 }
4386
4387 #[test]
4388 fn test_odd_syntax_append_no_trailing() {
4389 let mut relations: Relations = "\n foo\n , bar".parse().unwrap();
4390 let entry = Entry::from(Relation::simple("blah"));
4391 relations.add_dependency(entry, None);
4392 assert_eq!(relations.to_string(), "\n foo\n , bar\n , blah");
4393 }
4394
4395 #[test]
4396 fn test_odd_syntax_append_with_trailing() {
4397 let mut relations: Relations = "\n foo\n , bar\n".parse().unwrap();
4398 let entry = Entry::from(Relation::simple("blah"));
4399 relations.add_dependency(entry, None);
4400 assert_eq!(relations.to_string(), "\n foo\n , bar\n , blah");
4401 }
4402
4403 #[test]
4404 fn test_insert_at_1_no_trailing() {
4405 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4406 let entry = Entry::from(Relation::simple("blah"));
4407 relations.add_dependency(entry, Some(1));
4408 assert_eq!(relations.to_string(), "foo,\n blah,\n bar");
4409 }
4410
4411 #[test]
4412 fn test_insert_at_1_with_trailing() {
4413 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4414 let entry = Entry::from(Relation::simple("blah"));
4415 relations.add_dependency(entry, Some(1));
4416 assert_eq!(relations.to_string(), "foo,\n blah,\n bar");
4417 }
4418
4419 #[test]
4420 fn test_odd_syntax_insert_at_1() {
4421 let mut relations: Relations = "\n foo\n , bar\n".parse().unwrap();
4422 let entry = Entry::from(Relation::simple("blah"));
4423 relations.add_dependency(entry, Some(1));
4424 assert_eq!(relations.to_string(), "\n foo\n , blah\n , bar");
4425 }
4426
4427 #[test]
4428 fn test_relations_preserves_exact_whitespace() {
4429 let input =
4431 "debhelper (>= 10), quilt (>= 0.40),\n libsystemd-dev [linux-any], pkg-config";
4432
4433 let relations: Relations = input.parse().unwrap();
4434
4435 assert_eq!(
4437 relations.to_string(),
4438 input,
4439 "Relations should preserve exact whitespace from input"
4440 );
4441 }
4442
4443 #[test]
4444 fn test_remove_entry_preserves_indentation() {
4445 let input = "debhelper (>= 10), quilt (>= 0.40),\n libsystemd-dev [linux-any], dh-systemd (>= 1.5), pkg-config";
4447
4448 let mut relations: Relations = input.parse().unwrap();
4449
4450 let mut to_remove = Vec::new();
4452 for (idx, entry) in relations.entries().enumerate() {
4453 for relation in entry.relations() {
4454 if relation.name() == "dh-systemd" {
4455 to_remove.push(idx);
4456 break;
4457 }
4458 }
4459 }
4460
4461 for idx in to_remove.into_iter().rev() {
4462 relations.remove_entry(idx);
4463 }
4464
4465 let output = relations.to_string();
4466 println!("After removal: '{}'", output);
4467
4468 assert!(
4470 output.contains("\n libsystemd-dev"),
4471 "Expected 4-space indentation to be preserved, but got:\n'{}'",
4472 output
4473 );
4474 }
4475
4476 #[test]
4477 fn test_relation_is_implied_by_same_package() {
4478 let inner = Relation::new(
4480 "pkg",
4481 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4482 );
4483 let outer = Relation::new(
4484 "pkg",
4485 Some((VersionConstraint::GreaterThanEqual, "1.5".parse().unwrap())),
4486 );
4487 assert!(inner.is_implied_by(&outer));
4488 }
4489
4490 #[test]
4491 fn test_relation_is_implied_by_different_package() {
4492 let inner = Relation::new("pkg1", None);
4494 let outer = Relation::new("pkg2", None);
4495 assert!(!inner.is_implied_by(&outer));
4496 }
4497
4498 #[test]
4499 fn test_relation_is_implied_by_no_version() {
4500 let inner = Relation::new("pkg", None);
4502 let outer = Relation::new(
4503 "pkg",
4504 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4505 );
4506 assert!(inner.is_implied_by(&outer));
4507 }
4508
4509 #[test]
4510 fn test_relation_is_implied_by_identical() {
4511 let inner = Relation::new(
4513 "pkg",
4514 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4515 );
4516 let outer = Relation::new(
4517 "pkg",
4518 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4519 );
4520 assert!(inner.is_implied_by(&outer));
4521 assert!(outer.is_implied_by(&inner));
4522 }
4523
4524 #[test]
4525 fn test_relation_is_implied_by_greater_than_equal() {
4526 let inner = Relation::new(
4528 "pkg",
4529 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4530 );
4531 let outer = Relation::new(
4532 "pkg",
4533 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
4534 );
4535 assert!(inner.is_implied_by(&outer));
4536 assert!(!outer.is_implied_by(&inner));
4537
4538 let outer = Relation::new(
4540 "pkg",
4541 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4542 );
4543 assert!(inner.is_implied_by(&outer));
4544
4545 let outer = Relation::new(
4547 "pkg",
4548 Some((VersionConstraint::GreaterThan, "1.5".parse().unwrap())),
4549 );
4550 assert!(inner.is_implied_by(&outer));
4551
4552 let inner = Relation::new(
4554 "pkg",
4555 Some((VersionConstraint::GreaterThanEqual, "3.0".parse().unwrap())),
4556 );
4557 let outer = Relation::new(
4558 "pkg",
4559 Some((VersionConstraint::GreaterThan, "3.0".parse().unwrap())),
4560 );
4561 assert!(!inner.is_implied_by(&outer));
4562 }
4563
4564 #[test]
4565 fn test_relation_is_implied_by_less_than_equal() {
4566 let inner = Relation::new(
4568 "pkg",
4569 Some((VersionConstraint::LessThanEqual, "2.0".parse().unwrap())),
4570 );
4571 let outer = Relation::new(
4572 "pkg",
4573 Some((VersionConstraint::LessThanEqual, "1.0".parse().unwrap())),
4574 );
4575 assert!(inner.is_implied_by(&outer));
4576 assert!(!outer.is_implied_by(&inner));
4577
4578 let outer = Relation::new(
4580 "pkg",
4581 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4582 );
4583 assert!(inner.is_implied_by(&outer));
4584
4585 let outer = Relation::new(
4587 "pkg",
4588 Some((VersionConstraint::LessThan, "1.5".parse().unwrap())),
4589 );
4590 assert!(inner.is_implied_by(&outer));
4591 }
4592
4593 #[test]
4594 fn test_relation_is_implied_by_equal() {
4595 let inner = Relation::new(
4597 "pkg",
4598 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4599 );
4600 let outer = Relation::new(
4601 "pkg",
4602 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4603 );
4604 assert!(inner.is_implied_by(&outer));
4605
4606 let outer = Relation::new(
4608 "pkg",
4609 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4610 );
4611 assert!(!inner.is_implied_by(&outer));
4612
4613 let outer = Relation::new(
4615 "pkg",
4616 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4617 );
4618 assert!(!inner.is_implied_by(&outer));
4619 }
4620
4621 #[test]
4622 fn test_relation_is_implied_by_greater_than() {
4623 let inner = Relation::new(
4625 "pkg",
4626 Some((VersionConstraint::GreaterThan, "1.0".parse().unwrap())),
4627 );
4628 let outer = Relation::new(
4629 "pkg",
4630 Some((VersionConstraint::GreaterThan, "2.0".parse().unwrap())),
4631 );
4632 assert!(inner.is_implied_by(&outer));
4633
4634 let outer = Relation::new(
4636 "pkg",
4637 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4638 );
4639 assert!(inner.is_implied_by(&outer));
4640
4641 let outer = Relation::new(
4643 "pkg",
4644 Some((VersionConstraint::GreaterThanEqual, "1.5".parse().unwrap())),
4645 );
4646 assert!(inner.is_implied_by(&outer));
4647
4648 let outer = Relation::new(
4650 "pkg",
4651 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4652 );
4653 assert!(!inner.is_implied_by(&outer));
4654 }
4655
4656 #[test]
4657 fn test_relation_is_implied_by_less_than() {
4658 let inner = Relation::new(
4660 "pkg",
4661 Some((VersionConstraint::LessThan, "2.0".parse().unwrap())),
4662 );
4663 let outer = Relation::new(
4664 "pkg",
4665 Some((VersionConstraint::LessThan, "1.0".parse().unwrap())),
4666 );
4667 assert!(inner.is_implied_by(&outer));
4668
4669 let outer = Relation::new(
4671 "pkg",
4672 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4673 );
4674 assert!(inner.is_implied_by(&outer));
4675
4676 let outer = Relation::new(
4678 "pkg",
4679 Some((VersionConstraint::LessThanEqual, "1.5".parse().unwrap())),
4680 );
4681 assert!(inner.is_implied_by(&outer));
4682 }
4683
4684 #[test]
4685 fn test_relation_is_implied_by_incompatible_constraints() {
4686 let inner = Relation::new(
4688 "pkg",
4689 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4690 );
4691 let outer = Relation::new(
4692 "pkg",
4693 Some((VersionConstraint::LessThanEqual, "2.0".parse().unwrap())),
4694 );
4695 assert!(!inner.is_implied_by(&outer));
4696 assert!(!outer.is_implied_by(&inner));
4697 }
4698
4699 #[test]
4700 fn test_entry_is_implied_by_identical() {
4701 let inner: Entry = "pkg (>= 1.0)".parse().unwrap();
4702 let outer: Entry = "pkg (>= 1.0)".parse().unwrap();
4703 assert!(inner.is_implied_by(&outer));
4704 }
4705
4706 #[test]
4707 fn test_entry_is_implied_by_or_group() {
4708 let inner: Entry = "pkg (>= 1.0)".parse().unwrap();
4710 let outer: Entry = "pkg (>= 1.5) | libc6".parse().unwrap();
4711 assert!(inner.is_implied_by(&outer));
4712 }
4713
4714 #[test]
4715 fn test_entry_is_implied_by_simple_or() {
4716 let inner: Entry = "pkg1 | pkg2".parse().unwrap();
4718 let outer: Entry = "pkg1".parse().unwrap();
4719 assert!(inner.is_implied_by(&outer));
4720
4721 let outer: Entry = "pkg2".parse().unwrap();
4723 assert!(inner.is_implied_by(&outer));
4724 }
4725
4726 #[test]
4727 fn test_entry_is_implied_by_not_implied() {
4728 let inner: Entry = "pkg (>= 2.0)".parse().unwrap();
4730 let outer: Entry = "pkg (>= 1.0)".parse().unwrap();
4731 assert!(!inner.is_implied_by(&outer));
4732 }
4733
4734 #[test]
4735 fn test_entry_is_implied_by_different_packages() {
4736 let inner: Entry = "pkg1".parse().unwrap();
4737 let outer: Entry = "pkg2".parse().unwrap();
4738 assert!(!inner.is_implied_by(&outer));
4739 }
4740
4741 #[test]
4742 fn test_entry_is_implied_by_complex_or() {
4743 let inner: Entry = "pkg1 | pkg2".parse().unwrap();
4745 let outer: Entry = "pkg1 | pkg2".parse().unwrap();
4746 assert!(inner.is_implied_by(&outer));
4747
4748 let outer: Entry = "pkg1 | pkg2 | pkg3".parse().unwrap();
4750 assert!(inner.is_implied_by(&outer));
4751 }
4752
4753 #[test]
4754 fn test_parse_version_with_epoch() {
4755 let input = "amule-dbg (<< 1:2.3.2-2~)";
4758 let parsed: Relations = input.parse().unwrap();
4759 assert_eq!(parsed.to_string(), input);
4760 assert_eq!(parsed.entries().count(), 1);
4761 let entry = parsed.entries().next().unwrap();
4762 assert_eq!(entry.to_string(), "amule-dbg (<< 1:2.3.2-2~)");
4763 assert_eq!(entry.relations().count(), 1);
4764 let relation = entry.relations().next().unwrap();
4765 assert_eq!(relation.name(), "amule-dbg");
4766 assert_eq!(relation.to_string(), "amule-dbg (<< 1:2.3.2-2~)");
4767 assert_eq!(
4768 relation.version(),
4769 Some((VersionConstraint::LessThan, "1:2.3.2-2~".parse().unwrap()))
4770 );
4771 }
4772
4773 #[test]
4774 fn test_ensure_relation_add_new() {
4775 let mut relations: Relations = "python3".parse().unwrap();
4777 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4778 let added = relations.ensure_relation(new_entry);
4779 assert!(added);
4780 assert_eq!(relations.to_string(), "debhelper (>= 12), python3");
4782 }
4783
4784 #[test]
4785 fn test_ensure_relation_already_satisfied() {
4786 let mut relations: Relations = "debhelper (>= 13)".parse().unwrap();
4788 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4789 let added = relations.ensure_relation(new_entry);
4790 assert!(!added);
4791 assert_eq!(relations.to_string(), "debhelper (>= 13)");
4792 }
4793
4794 #[test]
4795 fn test_ensure_relation_replace_weaker() {
4796 let mut relations: Relations = "debhelper (>= 11)".parse().unwrap();
4798 let new_entry: Entry = "debhelper (>= 13)".parse().unwrap();
4799 let added = relations.ensure_relation(new_entry);
4800 assert!(added);
4801 assert_eq!(relations.to_string(), "debhelper (>= 13)");
4802 }
4803
4804 #[test]
4805 fn test_ensure_relation_replace_multiple_weaker() {
4806 let mut relations: Relations = "debhelper (>= 11), debhelper (>= 10), python3"
4808 .parse()
4809 .unwrap();
4810 let new_entry: Entry = "debhelper (>= 13)".parse().unwrap();
4811 let added = relations.ensure_relation(new_entry);
4812 assert!(added);
4813 assert_eq!(relations.to_string(), "debhelper (>= 13), python3");
4814 }
4815
4816 #[test]
4817 fn test_ensure_relation_identical_entry() {
4818 let mut relations: Relations = "debhelper (>= 12)".parse().unwrap();
4820 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4821 let added = relations.ensure_relation(new_entry);
4822 assert!(!added);
4823 assert_eq!(relations.to_string(), "debhelper (>= 12)");
4824 }
4825
4826 #[test]
4827 fn test_ensure_relation_no_version_constraint() {
4828 let mut relations: Relations = "python3".parse().unwrap();
4830 let new_entry: Entry = "debhelper".parse().unwrap();
4831 let added = relations.ensure_relation(new_entry);
4832 assert!(added);
4833 assert_eq!(relations.to_string(), "debhelper, python3");
4835 }
4836
4837 #[test]
4838 fn test_ensure_relation_strengthen_unversioned() {
4839 let mut relations: Relations = "debhelper".parse().unwrap();
4842 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4843 let added = relations.ensure_relation(new_entry);
4844 assert!(added);
4845 assert_eq!(relations.to_string(), "debhelper (>= 12)");
4846 }
4847
4848 #[test]
4849 fn test_ensure_relation_versioned_implies_unversioned() {
4850 let mut relations: Relations = "debhelper (>= 12)".parse().unwrap();
4853 let new_entry: Entry = "debhelper".parse().unwrap();
4854 let added = relations.ensure_relation(new_entry);
4855 assert!(!added);
4856 assert_eq!(relations.to_string(), "debhelper (>= 12)");
4857 }
4858
4859 #[test]
4860 fn test_ensure_relation_preserves_whitespace() {
4861 let mut relations: Relations = "python3, rustc".parse().unwrap();
4863 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4864 let added = relations.ensure_relation(new_entry);
4865 assert!(added);
4866 assert_eq!(relations.to_string(), "debhelper (>= 12), python3, rustc");
4868 }
4869
4870 #[test]
4871 fn test_ensure_relation_empty_relations() {
4872 let mut relations: Relations = Relations::new();
4874 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4875 let added = relations.ensure_relation(new_entry);
4876 assert!(added);
4877 assert_eq!(relations.to_string(), "debhelper (>= 12)");
4878 }
4879
4880 #[test]
4881 fn test_ensure_relation_alternative_dependencies() {
4882 let mut relations: Relations = "python3 | python3-minimal".parse().unwrap();
4884 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4885 let added = relations.ensure_relation(new_entry);
4886 assert!(added);
4887 assert_eq!(
4889 relations.to_string(),
4890 "debhelper (>= 12), python3 | python3-minimal"
4891 );
4892 }
4893
4894 #[test]
4895 fn test_ensure_relation_replace_in_middle() {
4896 let mut relations: Relations = "python3, debhelper (>= 11), rustc".parse().unwrap();
4898 let new_entry: Entry = "debhelper (>= 13)".parse().unwrap();
4899 let added = relations.ensure_relation(new_entry);
4900 assert!(added);
4901 assert_eq!(relations.to_string(), "python3, debhelper (>= 13), rustc");
4902 }
4903
4904 #[test]
4905 fn test_ensure_relation_with_different_package() {
4906 let mut relations: Relations = "python3, debhelper (>= 12)".parse().unwrap();
4908 let new_entry: Entry = "rustc".parse().unwrap();
4909 let added = relations.ensure_relation(new_entry);
4910 assert!(added);
4911 assert_eq!(relations.to_string(), "python3, debhelper (>= 12), rustc");
4912 }
4913
4914 #[test]
4915 fn test_parse_invalid_token_in_arch_list() {
4916 let input = "foo [>= bar]";
4917 let result: Result<Relations, _> = input.parse();
4918 assert!(
4919 result.is_err(),
4920 "Expected error for invalid token in architecture list"
4921 );
4922 }
4923
4924 #[test]
4925 fn test_parse_invalid_token_in_profile_list() {
4926 let input = "foo <[] baz>";
4927 let result: Result<Relations, _> = input.parse();
4928 assert!(
4929 result.is_err(),
4930 "Expected error for invalid token in profile list"
4931 );
4932 }
4933
4934 #[test]
4935 fn test_parse_relaxed_unterminated_arch_list() {
4936 let (relations, errors) = Relations::parse_relaxed("libc6 [", true);
4937 assert!(!errors.is_empty());
4938 assert_eq!(relations.to_string(), "libc6 [");
4939 }
4940
4941 #[test]
4942 fn test_parse_relaxed_partial_arch_name() {
4943 let (relations, errors) = Relations::parse_relaxed("libc6 [amd", true);
4944 assert!(!errors.is_empty());
4945 assert_eq!(relations.to_string(), "libc6 [amd");
4946 }
4947
4948 #[test]
4949 fn test_parse_relaxed_unterminated_profile_list() {
4950 let (relations, errors) = Relations::parse_relaxed("libc6 <cross", true);
4951 assert!(!errors.is_empty());
4952 assert_eq!(relations.to_string(), "libc6 <cross");
4953 }
4954
4955 #[test]
4956 fn test_parse_relaxed_unterminated_substvar() {
4957 let (relations, errors) = Relations::parse_relaxed("${shlibs:Depends", true);
4958 assert!(!errors.is_empty());
4959 assert_eq!(relations.to_string(), "${shlibs:Depends");
4960 }
4961
4962 #[test]
4963 fn test_parse_relaxed_empty_substvar() {
4964 let (relations, errors) = Relations::parse_relaxed("${", true);
4965 assert!(!errors.is_empty());
4966 assert_eq!(relations.to_string(), "${");
4967 }
4968}