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