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