1use crate::relations::SyntaxKind::{self, *};
22use crate::relations::{BuildProfile, VersionConstraint};
23use debversion::Version;
24use rowan::{Direction, NodeOrToken};
25use std::collections::HashSet;
26
27#[derive(Debug, Clone, PartialEq, Eq, Hash)]
29pub struct ParseError(Vec<String>);
30
31impl std::fmt::Display for ParseError {
32 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
33 for err in &self.0 {
34 writeln!(f, "{}", err)?;
35 }
36 Ok(())
37 }
38}
39
40impl std::error::Error for ParseError {}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
46pub enum Lang {}
47impl rowan::Language for Lang {
48 type Kind = SyntaxKind;
49 fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
50 unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
51 }
52 fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
53 kind.into()
54 }
55}
56
57use rowan::{GreenNode, GreenToken};
60
61use rowan::GreenNodeBuilder;
65
66struct Parse {
69 green_node: GreenNode,
70 #[allow(unused)]
71 errors: Vec<String>,
72}
73
74fn parse(text: &str, allow_substvar: bool) -> Parse {
75 struct Parser {
76 tokens: Vec<(SyntaxKind, String)>,
79 builder: GreenNodeBuilder<'static>,
81 errors: Vec<String>,
84 allow_substvar: bool,
86 }
87
88 impl Parser {
89 fn parse_substvar(&mut self) {
90 self.builder.start_node(SyntaxKind::SUBSTVAR.into());
91 self.bump();
92 if self.current() != Some(L_CURLY) {
93 self.error(format!("expected {{ but got {:?}", self.current()).to_string());
94 } else {
95 self.bump();
96 }
97 loop {
98 match self.current() {
99 Some(IDENT) | Some(COLON) => {
100 self.bump();
101 }
102 Some(R_CURLY) => {
103 break;
104 }
105 e => {
106 self.error(format!("expected identifier or : but got {:?}", e).to_string());
107 break;
108 }
109 }
110 }
111 if self.current() != Some(R_CURLY) {
112 self.error(format!("expected }} but got {:?}", self.current()).to_string());
113 } else {
114 self.bump();
115 }
116 self.builder.finish_node();
117 }
118
119 fn parse_entry(&mut self) {
120 self.skip_ws();
121 self.builder.start_node(SyntaxKind::ENTRY.into());
122 loop {
123 self.parse_relation();
124 match self.peek_past_ws() {
125 Some(COMMA) => {
126 break;
127 }
128 Some(PIPE) => {
129 self.skip_ws();
130 self.bump();
131 self.skip_ws();
132 }
133 None => {
134 self.skip_ws();
135 break;
136 }
137 _ => {
138 self.skip_ws();
139 self.builder.start_node(SyntaxKind::ERROR.into());
140 match self.tokens.pop() {
141 Some((k, t)) => {
142 self.builder.token(k.into(), t.as_str());
143 self.errors
144 .push(format!("Expected comma or pipe, not {:?}", (k, t)));
145 }
146 None => {
147 self.errors
148 .push("Expected comma or pipe, got end of file".to_string());
149 }
150 }
151 self.builder.finish_node();
152 }
153 }
154 }
155 self.builder.finish_node();
156 }
157
158 fn error(&mut self, error: String) {
159 self.errors.push(error);
160 self.builder.start_node(SyntaxKind::ERROR.into());
161 if self.current().is_some() {
162 self.bump();
163 }
164 self.builder.finish_node();
165 }
166
167 fn parse_relation(&mut self) {
168 self.builder.start_node(SyntaxKind::RELATION.into());
169 if self.current() == Some(IDENT) {
170 self.bump();
171 } else {
172 self.error("Expected package name".to_string());
173 }
174 match self.peek_past_ws() {
175 Some(COLON) => {
176 self.skip_ws();
177 self.builder.start_node(ARCHQUAL.into());
178 self.bump();
179 self.skip_ws();
180 if self.current() == Some(IDENT) {
181 self.bump();
182 } else {
183 self.error("Expected architecture name".to_string());
184 }
185 self.builder.finish_node();
186 self.skip_ws();
187 }
188 Some(PIPE) | Some(COMMA) => {}
189 None | Some(L_PARENS) | Some(L_BRACKET) | Some(L_ANGLE) => {
190 self.skip_ws();
191 }
192 e => {
193 self.skip_ws();
194 self.error(format!(
195 "Expected ':' or '|' or '[' or '<' or ',' but got {:?}",
196 e
197 ));
198 }
199 }
200
201 if self.peek_past_ws() == Some(L_PARENS) {
202 self.skip_ws();
203 self.builder.start_node(VERSION.into());
204 self.bump();
205 self.skip_ws();
206
207 self.builder.start_node(CONSTRAINT.into());
208
209 while self.current() == Some(L_ANGLE)
210 || self.current() == Some(R_ANGLE)
211 || self.current() == Some(EQUAL)
212 {
213 self.bump();
214 }
215
216 self.builder.finish_node();
217
218 self.skip_ws();
219
220 while matches!(self.current(), Some(IDENT) | Some(COLON)) {
223 self.bump();
224 }
225
226 if self.current() == Some(R_PARENS) {
227 self.bump();
228 } else {
229 self.error("Expected ')'".to_string());
230 }
231
232 self.builder.finish_node();
233 }
234
235 if self.peek_past_ws() == Some(L_BRACKET) {
236 self.skip_ws();
237 self.builder.start_node(ARCHITECTURES.into());
238 self.bump();
239 loop {
240 self.skip_ws();
241 match self.current() {
242 Some(NOT) => {
243 self.bump();
244 }
245 Some(IDENT) => {
246 self.bump();
247 }
248 Some(R_BRACKET) => {
249 self.bump();
250 break;
251 }
252 _ => {
253 self.error("Expected architecture name or '!' or ']'".to_string());
254 break;
255 }
256 }
257 }
258 self.builder.finish_node();
259 }
260
261 while self.peek_past_ws() == Some(L_ANGLE) {
262 self.skip_ws();
263 self.builder.start_node(PROFILES.into());
264 self.bump();
265
266 loop {
267 self.skip_ws();
268 match self.current() {
269 Some(IDENT) => {
270 self.bump();
271 }
272 Some(NOT) => {
273 self.bump();
274 self.skip_ws();
275 if self.current() == Some(IDENT) {
276 self.bump();
277 } else {
278 self.error("Expected profile".to_string());
279 }
280 }
281 Some(R_ANGLE) => {
282 self.bump();
283 break;
284 }
285 None => {
286 self.error("Expected profile or '>'".to_string());
287 break;
288 }
289 _ => {
290 self.error("Expected profile or '!' or '>'".to_string());
291 break;
292 }
293 }
294 }
295
296 self.builder.finish_node();
297 }
298
299 self.builder.finish_node();
300 }
301
302 fn parse(mut self) -> Parse {
303 self.builder.start_node(SyntaxKind::ROOT.into());
304
305 self.skip_ws();
306
307 while self.current().is_some() {
308 match self.current() {
309 Some(IDENT) => self.parse_entry(),
310 Some(DOLLAR) => {
311 if self.allow_substvar {
312 self.parse_substvar()
313 } else {
314 self.error("Substvars are not allowed".to_string());
315 }
316 }
317 Some(COMMA) => {
318 }
320 Some(c) => {
321 self.error(format!("expected $ or identifier but got {:?}", c));
322 }
323 None => {
324 self.error("expected identifier but got end of file".to_string());
325 }
326 }
327
328 self.skip_ws();
329 match self.current() {
330 Some(COMMA) => {
331 self.bump();
332 }
333 None => {
334 break;
335 }
336 c => {
337 self.error(format!("expected comma or end of file but got {:?}", c));
338 }
339 }
340 self.skip_ws();
341 }
342
343 self.builder.finish_node();
344 Parse {
346 green_node: self.builder.finish(),
347 errors: self.errors,
348 }
349 }
350 fn bump(&mut self) {
352 let (kind, text) = self.tokens.pop().unwrap();
353 self.builder.token(kind.into(), text.as_str());
354 }
355 fn current(&self) -> Option<SyntaxKind> {
357 self.tokens.last().map(|(kind, _)| *kind)
358 }
359 fn skip_ws(&mut self) {
360 while matches!(
361 self.current(),
362 Some(WHITESPACE) | Some(NEWLINE) | Some(COMMENT)
363 ) {
364 self.bump()
365 }
366 }
367
368 fn peek_past_ws(&self) -> Option<SyntaxKind> {
369 let mut i = self.tokens.len();
370 while i > 0 {
371 i -= 1;
372 match self.tokens[i].0 {
373 WHITESPACE | NEWLINE | COMMENT => {}
374 _ => return Some(self.tokens[i].0),
375 }
376 }
377 None
378 }
379 }
380
381 let mut tokens = crate::relations::lex(text);
382 tokens.reverse();
383 Parser {
384 tokens,
385 builder: GreenNodeBuilder::new(),
386 errors: Vec::new(),
387 allow_substvar,
388 }
389 .parse()
390}
391
392pub type SyntaxNode = rowan::SyntaxNode<Lang>;
400pub type SyntaxToken = rowan::SyntaxToken<Lang>;
402pub type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;
404
405impl Parse {
406 fn root_mut(&self) -> Relations {
407 Relations::cast(SyntaxNode::new_root_mut(self.green_node.clone())).unwrap()
408 }
409}
410
411macro_rules! ast_node {
412 ($ast:ident, $kind:ident) => {
413 #[repr(transparent)]
415 pub struct $ast(SyntaxNode);
416 impl $ast {
417 #[allow(unused)]
418 fn cast(node: SyntaxNode) -> Option<Self> {
419 if node.kind() == $kind {
420 Some(Self(node))
421 } else {
422 None
423 }
424 }
425
426 pub fn syntax(&self) -> &rowan::SyntaxNode<Lang> {
428 &self.0
429 }
430 }
431
432 impl std::fmt::Display for $ast {
433 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
434 f.write_str(&self.0.text().to_string())
435 }
436 }
437 };
438}
439
440ast_node!(Relations, ROOT);
441ast_node!(Entry, ENTRY);
442ast_node!(Relation, RELATION);
443ast_node!(Substvar, SUBSTVAR);
444
445impl PartialEq for Relations {
446 fn eq(&self, other: &Self) -> bool {
447 self.entries().collect::<Vec<_>>() == other.entries().collect::<Vec<_>>()
448 }
449}
450
451impl PartialEq for Entry {
452 fn eq(&self, other: &Self) -> bool {
453 self.relations().collect::<Vec<_>>() == other.relations().collect::<Vec<_>>()
454 }
455}
456
457impl PartialEq for Relation {
458 fn eq(&self, other: &Self) -> bool {
459 self.try_name() == other.try_name()
460 && self.version() == other.version()
461 && self.archqual() == other.archqual()
462 && self.architectures().map(|x| x.collect::<HashSet<_>>())
463 == other.architectures().map(|x| x.collect::<HashSet<_>>())
464 && self.profiles().eq(other.profiles())
465 }
466}
467
468#[cfg(feature = "serde")]
469impl serde::Serialize for Relations {
470 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
471 let rep = self.to_string();
472 serializer.serialize_str(&rep)
473 }
474}
475
476#[cfg(feature = "serde")]
477impl<'de> serde::Deserialize<'de> for Relations {
478 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
479 let s = String::deserialize(deserializer)?;
480 let relations = s.parse().map_err(serde::de::Error::custom)?;
481 Ok(relations)
482 }
483}
484
485impl std::fmt::Debug for Relations {
486 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
487 let mut s = f.debug_struct("Relations");
488
489 for entry in self.entries() {
490 s.field("entry", &entry);
491 }
492
493 s.finish()
494 }
495}
496
497impl std::fmt::Debug for Entry {
498 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
499 let mut s = f.debug_struct("Entry");
500
501 for relation in self.relations() {
502 s.field("relation", &relation);
503 }
504
505 s.finish()
506 }
507}
508
509#[cfg(feature = "serde")]
510impl serde::Serialize for Entry {
511 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
512 let rep = self.to_string();
513 serializer.serialize_str(&rep)
514 }
515}
516
517#[cfg(feature = "serde")]
518impl<'de> serde::Deserialize<'de> for Entry {
519 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
520 let s = String::deserialize(deserializer)?;
521 let entry = s.parse().map_err(serde::de::Error::custom)?;
522 Ok(entry)
523 }
524}
525
526impl std::fmt::Debug for Relation {
527 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
528 let mut s = f.debug_struct("Relation");
529
530 s.field("name", &self.try_name());
531
532 if let Some((vc, version)) = self.version() {
533 s.field("version", &vc);
534 s.field("version", &version);
535 }
536
537 s.finish()
538 }
539}
540
541#[cfg(feature = "serde")]
542impl serde::Serialize for Relation {
543 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
544 let rep = self.to_string();
545 serializer.serialize_str(&rep)
546 }
547}
548
549#[cfg(feature = "serde")]
550impl<'de> serde::Deserialize<'de> for Relation {
551 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
552 let s = String::deserialize(deserializer)?;
553 let relation = s.parse().map_err(serde::de::Error::custom)?;
554 Ok(relation)
555 }
556}
557
558impl Default for Relations {
559 fn default() -> Self {
560 Self::new()
561 }
562}
563
564fn is_special_package_name(name: &str) -> bool {
569 if name.starts_with("${") && name.ends_with('}') {
571 return true;
572 }
573 if name.starts_with('@') && name.ends_with('@') {
575 return true;
576 }
577 false
578}
579
580pub trait SortingOrder {
582 fn lt(&self, name1: &str, name2: &str) -> bool;
586
587 fn ignore(&self, name: &str) -> bool;
589}
590
591#[derive(Debug, Clone, Copy, Default)]
593pub struct DefaultSortingOrder;
594
595impl SortingOrder for DefaultSortingOrder {
596 fn lt(&self, name1: &str, name2: &str) -> bool {
597 let special1 = is_special_package_name(name1);
598 let special2 = is_special_package_name(name2);
599
600 if special1 && !special2 {
602 return false;
603 }
604 if !special1 && special2 {
605 return true;
606 }
607 if special1 && special2 {
608 return false;
610 }
611
612 name1 < name2
614 }
615
616 fn ignore(&self, name: &str) -> bool {
617 is_special_package_name(name)
618 }
619}
620
621#[derive(Debug, Clone, Copy, Default)]
631pub struct WrapAndSortOrder;
632
633impl WrapAndSortOrder {
634 const BUILD_SYSTEMS: &'static [&'static str] = &[
636 "cdbs",
637 "debhelper-compat",
638 "debhelper",
639 "debputy",
640 "dpkg-build-api",
641 "dpkg-dev",
642 ];
643
644 fn get_sort_key<'a>(&self, name: &'a str) -> (i32, &'a str) {
645 if Self::BUILD_SYSTEMS.contains(&name) || name.starts_with("dh-") {
647 return (-1, name);
648 }
649
650 if name
652 .chars()
653 .next()
654 .is_some_and(|c| c.is_ascii_lowercase() || c.is_ascii_digit())
655 {
656 return (0, name);
657 }
658
659 (1, name)
661 }
662}
663
664impl SortingOrder for WrapAndSortOrder {
665 fn lt(&self, name1: &str, name2: &str) -> bool {
666 self.get_sort_key(name1) < self.get_sort_key(name2)
667 }
668
669 fn ignore(&self, _name: &str) -> bool {
670 false
672 }
673}
674
675impl Relations {
676 pub fn new() -> Self {
678 Self::from(vec![])
679 }
680
681 #[must_use]
683 pub fn wrap_and_sort(self) -> Self {
684 let mut entries = self
685 .entries()
686 .map(|e| e.wrap_and_sort())
687 .collect::<Vec<_>>();
688 entries.sort();
689 Self::from(entries)
691 }
692
693 pub fn entries(&self) -> impl Iterator<Item = Entry> + '_ {
695 self.0.children().filter_map(Entry::cast)
696 }
697
698 pub fn iter(&self) -> impl Iterator<Item = Entry> + '_ {
700 self.entries()
701 }
702
703 pub fn get_entry(&self, idx: usize) -> Option<Entry> {
705 self.entries().nth(idx)
706 }
707
708 pub fn remove_entry(&mut self, idx: usize) -> Entry {
710 let mut entry = self.get_entry(idx).unwrap();
711 entry.remove();
712 entry
713 }
714
715 fn collect_whitespace(start: Option<NodeOrToken<SyntaxNode, SyntaxToken>>) -> String {
717 let mut pattern = String::new();
718 let mut current = start;
719 while let Some(token) = current {
720 if matches!(token.kind(), WHITESPACE | NEWLINE | COMMENT) {
721 if let NodeOrToken::Token(t) = &token {
722 pattern.push_str(t.text());
723 }
724 current = token.next_sibling_or_token();
725 } else {
726 break;
727 }
728 }
729 pattern
730 }
731
732 fn to_green(node: &NodeOrToken<SyntaxNode, SyntaxToken>) -> NodeOrToken<GreenNode, GreenToken> {
734 match node {
735 NodeOrToken::Node(n) => NodeOrToken::Node(n.green().into()),
736 NodeOrToken::Token(t) => NodeOrToken::Token(t.green().to_owned()),
737 }
738 }
739
740 fn is_whitespace_token(token: &GreenToken) -> bool {
742 token.kind() == rowan::SyntaxKind(WHITESPACE as u16)
743 || token.kind() == rowan::SyntaxKind(NEWLINE as u16)
744 || token.kind() == rowan::SyntaxKind(COMMENT as u16)
745 }
746
747 fn strip_trailing_ws_from_children(
749 mut children: Vec<NodeOrToken<GreenNode, GreenToken>>,
750 ) -> Vec<NodeOrToken<GreenNode, GreenToken>> {
751 while let Some(last) = children.last() {
752 if let NodeOrToken::Token(t) = last {
753 if Self::is_whitespace_token(t) {
754 children.pop();
755 } else {
756 break;
757 }
758 } else {
759 break;
760 }
761 }
762 children
763 }
764
765 fn strip_relation_trailing_ws(relation: &SyntaxNode) -> GreenNode {
767 let children: Vec<_> = relation
768 .children_with_tokens()
769 .map(|c| Self::to_green(&c))
770 .collect();
771 let stripped = Self::strip_trailing_ws_from_children(children);
772 GreenNode::new(relation.kind().into(), stripped)
773 }
774
775 fn build_odd_syntax_nodes(
777 before_ws: &str,
778 after_ws: &str,
779 ) -> Vec<NodeOrToken<GreenNode, GreenToken>> {
780 [
781 (!before_ws.is_empty())
782 .then(|| NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), before_ws))),
783 Some(NodeOrToken::Token(GreenToken::new(COMMA.into(), ","))),
784 (!after_ws.is_empty())
785 .then(|| NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), after_ws))),
786 ]
787 .into_iter()
788 .flatten()
789 .collect()
790 }
791
792 fn detect_odd_syntax(&self) -> Option<(String, String)> {
794 for entry_node in self.entries() {
795 let mut node = entry_node.0.next_sibling_or_token()?;
796
797 let mut before = String::new();
799 while matches!(node.kind(), WHITESPACE | NEWLINE | COMMENT) {
800 if let NodeOrToken::Token(t) = &node {
801 before.push_str(t.text());
802 }
803 node = node.next_sibling_or_token()?;
804 }
805
806 if node.kind() == COMMA && !before.is_empty() {
808 let after = Self::collect_whitespace(node.next_sibling_or_token());
809 return Some((before, after));
810 }
811 }
812 None
813 }
814
815 fn detect_whitespace_pattern(&self, default: &str) -> String {
823 use std::collections::HashMap;
824
825 let entries: Vec<_> = self.entries().collect();
826 let num_entries = entries.len();
827
828 if num_entries == 0 {
829 if self.substvars().next().is_some() {
831 return default.to_string();
833 }
834 return String::from(""); }
836
837 if num_entries == 1 {
838 if let Some(node) = entries[0].0.next_sibling_or_token() {
840 if node.kind() == COMMA {
841 let pattern = Self::collect_whitespace(node.next_sibling_or_token());
842 if !pattern.is_empty() {
843 return pattern;
844 }
845 }
846 }
847 return default.to_string(); }
849
850 let mut whitespace_counts: HashMap<String, usize> = HashMap::new();
852
853 for (i, entry) in entries.iter().enumerate() {
854 if i == num_entries - 1 {
855 break; }
857
858 if let Some(mut node) = entry.0.next_sibling_or_token() {
860 while matches!(node.kind(), WHITESPACE | NEWLINE | COMMENT) {
862 if let Some(next) = node.next_sibling_or_token() {
863 node = next;
864 } else {
865 break;
866 }
867 }
868
869 if node.kind() == COMMA {
871 let pattern = Self::collect_whitespace(node.next_sibling_or_token());
872 if !pattern.is_empty() {
873 *whitespace_counts.entry(pattern).or_insert(0) += 1;
874 }
875 }
876 }
877 }
878
879 if whitespace_counts.len() == 1 {
881 if let Some((ws, _)) = whitespace_counts.iter().next() {
882 return ws.clone();
883 }
884 }
885
886 if let Some((ws, _)) = whitespace_counts.iter().max_by_key(|(_, count)| *count) {
888 return ws.clone();
889 }
890
891 default.to_string()
893 }
894
895 pub fn insert_with_separator(&mut self, idx: usize, entry: Entry, default_sep: Option<&str>) {
902 let is_empty = self.entries().next().is_none();
903 let whitespace = self.detect_whitespace_pattern(default_sep.unwrap_or(" "));
904
905 self.strip_trailing_whitespace();
907
908 let odd_syntax = self.detect_odd_syntax();
910
911 let (position, new_children) = if let Some(current_entry) = self.entries().nth(idx) {
912 let to_insert = if idx == 0 && is_empty {
913 vec![entry.0.green().into()]
914 } else if let Some((before_ws, after_ws)) = &odd_syntax {
915 let mut nodes = vec![entry.0.green().into()];
916 nodes.extend(Self::build_odd_syntax_nodes(before_ws, after_ws));
917 nodes
918 } else {
919 vec![
920 entry.0.green().into(),
921 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
922 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), whitespace.as_str())),
923 ]
924 };
925
926 (current_entry.0.index(), to_insert)
927 } else {
928 let child_count = self.0.children_with_tokens().count();
929 let to_insert = if idx == 0 {
930 vec![entry.0.green().into()]
931 } else if let Some((before_ws, after_ws)) = &odd_syntax {
932 let mut nodes = Self::build_odd_syntax_nodes(before_ws, after_ws);
933 nodes.push(entry.0.green().into());
934 nodes
935 } else {
936 vec![
937 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
938 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), whitespace.as_str())),
939 entry.0.green().into(),
940 ]
941 };
942
943 (child_count, to_insert)
944 };
945 self.0 = SyntaxNode::new_root_mut(
947 self.0.replace_with(
948 self.0
949 .green()
950 .splice_children(position..position, new_children),
951 ),
952 );
953 }
954
955 pub fn insert(&mut self, idx: usize, entry: Entry) {
957 self.insert_with_separator(idx, entry, None);
958 }
959
960 fn strip_entry_trailing_ws(entry: &SyntaxNode) -> GreenNode {
962 let mut children: Vec<_> = entry
963 .children_with_tokens()
964 .map(|c| Self::to_green(&c))
965 .collect();
966
967 if let Some(NodeOrToken::Node(last)) = children.last() {
969 if last.kind() == rowan::SyntaxKind(RELATION as u16) {
970 let relation_node = entry.children().last().unwrap();
972 children.pop();
973 children.push(NodeOrToken::Node(Self::strip_relation_trailing_ws(
974 &relation_node,
975 )));
976 }
977 }
978
979 let stripped = Self::strip_trailing_ws_from_children(children);
981 GreenNode::new(ENTRY.into(), stripped)
982 }
983
984 fn strip_trailing_whitespace(&mut self) {
985 let mut children: Vec<_> = self
986 .0
987 .children_with_tokens()
988 .map(|c| Self::to_green(&c))
989 .collect();
990
991 if let Some(NodeOrToken::Node(last)) = children.last() {
993 if last.kind() == rowan::SyntaxKind(ENTRY as u16) {
994 let last_entry = self.0.children().last().unwrap();
995 children.pop();
996 children.push(NodeOrToken::Node(Self::strip_entry_trailing_ws(
997 &last_entry,
998 )));
999 }
1000 }
1001
1002 let stripped = Self::strip_trailing_ws_from_children(children);
1004
1005 let nc = self.0.children_with_tokens().count();
1006 self.0 = SyntaxNode::new_root_mut(
1007 self.0
1008 .replace_with(self.0.green().splice_children(0..nc, stripped)),
1009 );
1010 }
1011
1012 pub fn replace(&mut self, idx: usize, entry: Entry) {
1014 let current_entry = self.get_entry(idx).unwrap();
1015 self.0.splice_children(
1016 current_entry.0.index()..current_entry.0.index() + 1,
1017 vec![entry.0.into()],
1018 );
1019 }
1020
1021 pub fn push(&mut self, entry: Entry) {
1023 let pos = self.entries().count();
1024 self.insert(pos, entry);
1025 }
1026
1027 pub fn substvars(&self) -> impl Iterator<Item = String> + '_ {
1029 self.0
1030 .children()
1031 .filter_map(Substvar::cast)
1032 .map(|s| s.to_string())
1033 }
1034
1035 pub fn parse_relaxed(s: &str, allow_substvar: bool) -> (Relations, Vec<String>) {
1037 let parse = parse(s, allow_substvar);
1038 (parse.root_mut(), parse.errors)
1039 }
1040
1041 pub fn satisfied_by(&self, package_version: impl crate::VersionLookup + Copy) -> bool {
1043 self.entries().all(|e| e.satisfied_by(package_version))
1044 }
1045
1046 pub fn is_empty(&self) -> bool {
1048 self.entries().count() == 0
1049 }
1050
1051 pub fn len(&self) -> usize {
1053 self.entries().count()
1054 }
1055
1056 pub fn ensure_minimum_version(&mut self, package: &str, minimum_version: &Version) {
1079 let mut found = false;
1080 let mut obsolete_indices = vec![];
1081 let mut update_idx = None;
1082
1083 let entries: Vec<_> = self.entries().collect();
1084 for (idx, entry) in entries.iter().enumerate() {
1085 let relations: Vec<_> = entry.relations().collect();
1086
1087 let names: Vec<_> = relations.iter().filter_map(|r| r.try_name()).collect();
1089 if names.len() > 1 && names.contains(&package.to_string()) {
1090 let is_obsolete = relations.iter().any(|r| {
1092 if r.try_name().as_deref() != Some(package) {
1093 return false;
1094 }
1095 if let Some((vc, ver)) = r.version() {
1096 matches!(vc, VersionConstraint::GreaterThan if &ver < minimum_version)
1097 || matches!(vc, VersionConstraint::GreaterThanEqual if &ver <= minimum_version)
1098 } else {
1099 false
1100 }
1101 });
1102 if is_obsolete {
1103 obsolete_indices.push(idx);
1104 }
1105 continue;
1106 }
1107
1108 if names.len() == 1 && names[0] == package {
1110 found = true;
1111 let relation = relations.into_iter().next().unwrap();
1112
1113 let should_update = if let Some((vc, ver)) = relation.version() {
1115 match vc {
1116 VersionConstraint::GreaterThanEqual | VersionConstraint::GreaterThan => {
1117 &ver < minimum_version
1118 }
1119 _ => false,
1120 }
1121 } else {
1122 true
1123 };
1124
1125 if should_update {
1126 update_idx = Some(idx);
1127 }
1128 break;
1129 }
1130 }
1131
1132 if let Some(idx) = update_idx {
1134 let relation = Relation::new(
1135 package,
1136 Some((VersionConstraint::GreaterThanEqual, minimum_version.clone())),
1137 );
1138 let mut entry = self.get_entry(idx).unwrap();
1140 entry.replace(0, relation);
1141 self.replace(idx, entry);
1142 }
1143
1144 for idx in obsolete_indices.into_iter().rev() {
1146 self.remove_entry(idx);
1147 }
1148
1149 if !found {
1151 let relation = Relation::new(
1152 package,
1153 Some((VersionConstraint::GreaterThanEqual, minimum_version.clone())),
1154 );
1155 self.push(Entry::from(relation));
1156 }
1157 }
1158
1159 pub fn ensure_exact_version(&mut self, package: &str, version: &Version) {
1174 let mut found = false;
1175 let mut update_idx = None;
1176
1177 let entries: Vec<_> = self.entries().collect();
1178 for (idx, entry) in entries.iter().enumerate() {
1179 let relations: Vec<_> = entry.relations().collect();
1180 let names: Vec<_> = relations.iter().filter_map(|r| r.try_name()).collect();
1181
1182 if names.len() > 1 && names[0] == package {
1183 panic!("Complex rule for {}, aborting", package);
1184 }
1185
1186 if names.len() == 1 && names[0] == package {
1187 found = true;
1188 let relation = relations.into_iter().next().unwrap();
1189
1190 let should_update = if let Some((vc, ver)) = relation.version() {
1191 vc != VersionConstraint::Equal || &ver != version
1192 } else {
1193 true
1194 };
1195
1196 if should_update {
1197 update_idx = Some(idx);
1198 }
1199 break;
1200 }
1201 }
1202
1203 if let Some(idx) = update_idx {
1205 let relation =
1206 Relation::new(package, Some((VersionConstraint::Equal, version.clone())));
1207 let mut entry = self.get_entry(idx).unwrap();
1209 entry.replace(0, relation);
1210 self.replace(idx, entry);
1211 }
1212
1213 if !found {
1214 let relation =
1215 Relation::new(package, Some((VersionConstraint::Equal, version.clone())));
1216 self.push(Entry::from(relation));
1217 }
1218 }
1219
1220 pub fn ensure_some_version(&mut self, package: &str) {
1238 for entry in self.entries() {
1239 let relations: Vec<_> = entry.relations().collect();
1240 let names: Vec<_> = relations.iter().filter_map(|r| r.try_name()).collect();
1241
1242 if names.len() > 1 && names[0] == package {
1243 panic!("Complex rule for {}, aborting", package);
1244 }
1245
1246 if names.len() == 1 && names[0] == package {
1247 return;
1249 }
1250 }
1251
1252 let relation = Relation::simple(package);
1254 self.push(Entry::from(relation));
1255 }
1256
1257 pub fn ensure_relation(&mut self, new_entry: Entry) -> bool {
1282 let mut to_replace: Vec<usize> = Vec::new();
1283 let mut to_remove: Vec<usize> = Vec::new();
1284 let mut already_satisfied = false;
1285
1286 for (idx, existing_entry) in self.entries().enumerate() {
1288 if new_entry.is_implied_by(&existing_entry) {
1289 already_satisfied = true;
1291 break;
1292 }
1293 if existing_entry.is_implied_by(&new_entry) {
1294 if to_replace.is_empty() {
1297 to_replace.push(idx);
1298 } else {
1299 to_remove.push(idx);
1300 }
1301 }
1302 }
1303
1304 if already_satisfied {
1305 return false;
1306 }
1307
1308 for idx in to_remove.into_iter().rev() {
1310 self.remove_entry(idx);
1311 }
1312
1313 if let Some(&idx) = to_replace.first() {
1315 self.replace(idx, new_entry);
1316 } else {
1317 self.add_dependency(new_entry, None);
1318 }
1319
1320 true
1321 }
1322
1323 pub fn ensure_substvar(&mut self, substvar: &str) -> Result<(), String> {
1343 for existing in self.substvars() {
1345 if existing.trim() == substvar.trim() {
1346 return Ok(());
1347 }
1348 }
1349
1350 let (parsed, errors) = Relations::parse_relaxed(substvar, true);
1352 if !errors.is_empty() {
1353 return Err(errors.join("\n"));
1354 }
1355
1356 let whitespace = self.detect_whitespace_pattern(" ");
1358
1359 for substvar_node in parsed.0.children().filter(|n| n.kind() == SUBSTVAR) {
1361 let has_content = self.entries().next().is_some() || self.substvars().next().is_some();
1362
1363 let mut builder = GreenNodeBuilder::new();
1364 builder.start_node(ROOT.into());
1365
1366 for child in self.0.children_with_tokens() {
1368 match child {
1369 NodeOrToken::Node(n) => inject(&mut builder, n),
1370 NodeOrToken::Token(t) => builder.token(t.kind().into(), t.text()),
1371 }
1372 }
1373
1374 if has_content {
1376 builder.token(COMMA.into(), ",");
1377 builder.token(WHITESPACE.into(), whitespace.as_str());
1378 }
1379
1380 inject(&mut builder, substvar_node);
1382
1383 builder.finish_node();
1384 self.0 = SyntaxNode::new_root_mut(builder.finish());
1385 }
1386
1387 Ok(())
1388 }
1389
1390 pub fn drop_substvar(&mut self, substvar: &str) {
1407 let substvars_to_remove: Vec<_> = self
1409 .0
1410 .children()
1411 .filter_map(Substvar::cast)
1412 .filter(|s| s.to_string().trim() == substvar.trim())
1413 .collect();
1414
1415 for substvar_node in substvars_to_remove {
1416 let is_first = !substvar_node
1418 .0
1419 .siblings(Direction::Prev)
1420 .skip(1)
1421 .any(|n| n.kind() == ENTRY || n.kind() == SUBSTVAR);
1422
1423 let mut removed_comma = false;
1424
1425 while let Some(n) = substvar_node.0.next_sibling_or_token() {
1427 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
1428 n.detach();
1429 } else if n.kind() == COMMA {
1430 n.detach();
1431 removed_comma = true;
1432 break;
1433 } else {
1434 break;
1435 }
1436 }
1437
1438 if !is_first {
1440 while let Some(n) = substvar_node.0.prev_sibling_or_token() {
1441 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
1442 n.detach();
1443 } else if !removed_comma && n.kind() == COMMA {
1444 n.detach();
1445 break;
1446 } else {
1447 break;
1448 }
1449 }
1450 } else {
1451 while let Some(n) = substvar_node.0.next_sibling_or_token() {
1453 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
1454 n.detach();
1455 } else {
1456 break;
1457 }
1458 }
1459 }
1460
1461 substvar_node.0.detach();
1463 }
1464 }
1465
1466 pub fn filter_entries<F>(&mut self, keep: F)
1482 where
1483 F: Fn(&Entry) -> bool,
1484 {
1485 let indices_to_remove: Vec<_> = self
1486 .entries()
1487 .enumerate()
1488 .filter_map(|(idx, entry)| if keep(&entry) { None } else { Some(idx) })
1489 .collect();
1490
1491 for idx in indices_to_remove.into_iter().rev() {
1493 self.remove_entry(idx);
1494 }
1495 }
1496
1497 pub fn is_sorted(&self, sorting_order: &impl SortingOrder) -> bool {
1513 let mut last_name: Option<String> = None;
1514 for entry in self.entries() {
1515 let mut relations = entry.relations();
1517 let Some(relation) = relations.next() else {
1518 continue;
1519 };
1520
1521 let Some(name) = relation.try_name() else {
1522 continue;
1523 };
1524
1525 if sorting_order.ignore(&name) {
1527 continue;
1528 }
1529
1530 if let Some(ref last) = last_name {
1532 if sorting_order.lt(&name, last) {
1533 return false;
1534 }
1535 }
1536
1537 last_name = Some(name);
1538 }
1539 true
1540 }
1541
1542 fn find_insert_position(&self, entry: &Entry) -> usize {
1554 let Some(relation) = entry.relations().next() else {
1556 return self.len();
1558 };
1559 let Some(package_name) = relation.try_name() else {
1560 return self.len();
1561 };
1562
1563 let count = self.entries().filter(|e| !e.is_empty()).count();
1565
1566 let sorting_order: Box<dyn SortingOrder> = if count < 2 {
1568 Box::new(WrapAndSortOrder)
1569 } else {
1570 if self.is_sorted(&WrapAndSortOrder) {
1573 Box::new(WrapAndSortOrder)
1574 } else if self.is_sorted(&DefaultSortingOrder) {
1575 Box::new(DefaultSortingOrder)
1576 } else {
1577 return self.len();
1579 }
1580 };
1581
1582 if sorting_order.ignore(&package_name) {
1584 return self.len();
1585 }
1586
1587 let mut position = 0;
1589 for (idx, existing_entry) in self.entries().enumerate() {
1590 let mut existing_relations = existing_entry.relations();
1591 let Some(existing_relation) = existing_relations.next() else {
1592 position += 1;
1594 continue;
1595 };
1596
1597 let Some(existing_name) = existing_relation.try_name() else {
1598 position += 1;
1599 continue;
1600 };
1601
1602 if sorting_order.ignore(&existing_name) {
1604 position += 1;
1605 continue;
1606 }
1607
1608 if sorting_order.lt(&package_name, &existing_name) {
1610 return idx;
1611 }
1612 position += 1;
1613 }
1614
1615 position
1616 }
1617
1618 pub fn drop_dependency(&mut self, package: &str) -> bool {
1636 let indices_to_remove: Vec<_> = self
1637 .entries()
1638 .enumerate()
1639 .filter_map(|(idx, entry)| {
1640 let relations: Vec<_> = entry.relations().collect();
1641 let names: Vec<_> = relations.iter().filter_map(|r| r.try_name()).collect();
1642 if names == vec![package] {
1643 Some(idx)
1644 } else {
1645 None
1646 }
1647 })
1648 .collect();
1649
1650 let found = !indices_to_remove.is_empty();
1651
1652 for idx in indices_to_remove.into_iter().rev() {
1654 self.remove_entry(idx);
1655 }
1656
1657 found
1658 }
1659
1660 pub fn add_dependency(&mut self, entry: Entry, position: Option<usize>) {
1681 let pos = position.unwrap_or_else(|| self.find_insert_position(&entry));
1682 self.insert(pos, entry);
1683 }
1684
1685 pub fn get_relation(&self, package: &str) -> Result<(usize, Entry), String> {
1709 for (idx, entry) in self.entries().enumerate() {
1710 let relations: Vec<_> = entry.relations().collect();
1711 let names: Vec<_> = relations.iter().filter_map(|r| r.try_name()).collect();
1712
1713 if names.len() > 1 && names.contains(&package.to_string()) {
1714 return Err(format!("Complex rule for {}, aborting", package));
1715 }
1716
1717 if names.len() == 1 && names[0] == package {
1718 return Ok((idx, entry));
1719 }
1720 }
1721 Err(format!("Package {} not found", package))
1722 }
1723
1724 pub fn iter_relations_for(&self, package: &str) -> impl Iterator<Item = (usize, Entry)> + '_ {
1741 let package = package.to_string();
1742 self.entries().enumerate().filter(move |(_, entry)| {
1743 let names: Vec<_> = entry.relations().filter_map(|r| r.try_name()).collect();
1744 names.contains(&package)
1745 })
1746 }
1747
1748 pub fn has_relation(&self, package: &str) -> bool {
1765 self.entries().any(|entry| {
1766 entry
1767 .relations()
1768 .any(|r| r.try_name().as_deref() == Some(package))
1769 })
1770 }
1771}
1772
1773impl From<Vec<Entry>> for Relations {
1774 fn from(entries: Vec<Entry>) -> Self {
1775 let mut builder = GreenNodeBuilder::new();
1776 builder.start_node(ROOT.into());
1777 for (i, entry) in entries.into_iter().enumerate() {
1778 if i > 0 {
1779 builder.token(COMMA.into(), ",");
1780 builder.token(WHITESPACE.into(), " ");
1781 }
1782 inject(&mut builder, entry.0);
1783 }
1784 builder.finish_node();
1785 Relations(SyntaxNode::new_root_mut(builder.finish()))
1786 }
1787}
1788
1789impl From<Entry> for Relations {
1790 fn from(entry: Entry) -> Self {
1791 Self::from(vec![entry])
1792 }
1793}
1794
1795impl Default for Entry {
1796 fn default() -> Self {
1797 Self::new()
1798 }
1799}
1800
1801impl PartialOrd for Entry {
1802 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1803 Some(self.cmp(other))
1804 }
1805}
1806
1807impl Eq for Entry {}
1808
1809impl Ord for Entry {
1810 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1811 let mut rels_a = self.relations();
1812 let mut rels_b = other.relations();
1813 while let (Some(a), Some(b)) = (rels_a.next(), rels_b.next()) {
1814 match a.cmp(&b) {
1815 std::cmp::Ordering::Equal => continue,
1816 x => return x,
1817 }
1818 }
1819
1820 if rels_a.next().is_some() {
1821 return std::cmp::Ordering::Greater;
1822 }
1823
1824 if rels_b.next().is_some() {
1825 return std::cmp::Ordering::Less;
1826 }
1827
1828 std::cmp::Ordering::Equal
1829 }
1830}
1831
1832impl Entry {
1833 pub fn new() -> Self {
1835 let mut builder = GreenNodeBuilder::new();
1836 builder.start_node(SyntaxKind::ENTRY.into());
1837 builder.finish_node();
1838 Entry(SyntaxNode::new_root_mut(builder.finish()))
1839 }
1840
1841 pub fn replace(&mut self, idx: usize, relation: Relation) {
1843 let current_relation = self.get_relation(idx).unwrap();
1844
1845 let old_root = current_relation.0;
1846 let new_root = relation.0;
1847 let mut prev = new_root.first_child_or_token();
1849 let mut new_head_len = 0;
1850 while let Some(p) = prev {
1852 if p.kind() == WHITESPACE || p.kind() == NEWLINE {
1853 new_head_len += 1;
1854 prev = p.next_sibling_or_token();
1855 } else {
1856 break;
1857 }
1858 }
1859 let mut new_tail_len = 0;
1860 let mut next = new_root.last_child_or_token();
1861 while let Some(n) = next {
1862 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1863 new_tail_len += 1;
1864 next = n.prev_sibling_or_token();
1865 } else {
1866 break;
1867 }
1868 }
1869 let mut prev = old_root.first_child_or_token();
1871 let mut old_head = vec![];
1872 while let Some(p) = prev {
1873 if p.kind() == WHITESPACE || p.kind() == NEWLINE {
1874 old_head.push(p.clone());
1875 prev = p.next_sibling_or_token();
1876 } else {
1877 break;
1878 }
1879 }
1880 let mut old_tail = vec![];
1881 let mut next = old_root.last_child_or_token();
1882 while let Some(n) = next {
1883 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1884 old_tail.push(n.clone());
1885 next = n.prev_sibling_or_token();
1886 } else {
1887 break;
1888 }
1889 }
1890 new_root.splice_children(0..new_head_len, old_head);
1891 let tail_pos = new_root.children_with_tokens().count() - new_tail_len;
1892 new_root.splice_children(
1893 tail_pos - new_tail_len..tail_pos,
1894 old_tail.into_iter().rev(),
1895 );
1896 let index = old_root.index();
1897 self.0
1898 .splice_children(index..index + 1, vec![new_root.into()]);
1899 }
1900
1901 #[must_use]
1903 pub fn wrap_and_sort(&self) -> Self {
1904 let mut relations = self
1905 .relations()
1906 .map(|r| r.wrap_and_sort())
1907 .collect::<Vec<_>>();
1908 relations.sort();
1910 Self::from(relations)
1911 }
1912
1913 pub fn relations(&self) -> impl Iterator<Item = Relation> + '_ {
1915 self.0.children().filter_map(Relation::cast)
1916 }
1917
1918 pub fn iter(&self) -> impl Iterator<Item = Relation> + '_ {
1920 self.relations()
1921 }
1922
1923 pub fn get_relation(&self, idx: usize) -> Option<Relation> {
1925 self.relations().nth(idx)
1926 }
1927
1928 pub fn remove_relation(&self, idx: usize) -> Relation {
1941 let mut relation = self.get_relation(idx).unwrap();
1942 relation.remove();
1943 relation
1944 }
1945
1946 pub fn satisfied_by(&self, package_version: impl crate::VersionLookup + Copy) -> bool {
1962 self.relations().any(|r| {
1963 let Some(name) = r.try_name() else {
1964 return false;
1965 };
1966 let actual = package_version.lookup_version(name.as_str());
1967 if let Some((vc, version)) = r.version() {
1968 if let Some(actual) = actual {
1969 match vc {
1970 VersionConstraint::GreaterThanEqual => *actual >= version,
1971 VersionConstraint::LessThanEqual => *actual <= version,
1972 VersionConstraint::Equal => *actual == version,
1973 VersionConstraint::GreaterThan => *actual > version,
1974 VersionConstraint::LessThan => *actual < version,
1975 }
1976 } else {
1977 false
1978 }
1979 } else {
1980 actual.is_some()
1981 }
1982 })
1983 }
1984
1985 pub fn remove(&mut self) {
1996 let mut removed_comma = false;
1997 let is_first = !self
1998 .0
1999 .siblings(Direction::Prev)
2000 .skip(1)
2001 .any(|n| n.kind() == ENTRY);
2002 while let Some(n) = self.0.next_sibling_or_token() {
2003 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
2004 n.detach();
2005 } else if n.kind() == COMMA {
2006 n.detach();
2007 removed_comma = true;
2008 break;
2009 } else {
2010 panic!("Unexpected node: {:?}", n);
2011 }
2012 }
2013 if !is_first {
2014 while let Some(n) = self.0.prev_sibling_or_token() {
2015 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
2016 n.detach();
2017 } else if !removed_comma && n.kind() == COMMA {
2018 n.detach();
2019 break;
2020 } else {
2021 break;
2022 }
2023 }
2024 } else {
2025 while let Some(n) = self.0.next_sibling_or_token() {
2026 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
2027 n.detach();
2028 } else {
2029 break;
2030 }
2031 }
2032 }
2033 self.0.detach();
2034 }
2035
2036 pub fn is_empty(&self) -> bool {
2038 self.relations().count() == 0
2039 }
2040
2041 pub fn len(&self) -> usize {
2043 self.relations().count()
2044 }
2045
2046 pub fn push(&mut self, relation: Relation) {
2059 let is_empty = !self
2060 .0
2061 .children_with_tokens()
2062 .any(|n| n.kind() == PIPE || n.kind() == RELATION);
2063
2064 let (position, new_children) = if let Some(current_relation) = self.relations().last() {
2065 let to_insert: Vec<NodeOrToken<GreenNode, GreenToken>> = if is_empty {
2066 vec![relation.0.green().into()]
2067 } else {
2068 vec![
2069 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
2070 NodeOrToken::Token(GreenToken::new(PIPE.into(), "|")),
2071 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
2072 relation.0.green().into(),
2073 ]
2074 };
2075
2076 (current_relation.0.index() + 1, to_insert)
2077 } else {
2078 let child_count = self.0.children_with_tokens().count();
2079 (
2080 child_count,
2081 if is_empty {
2082 vec![relation.0.green().into()]
2083 } else {
2084 vec![
2085 NodeOrToken::Token(GreenToken::new(PIPE.into(), "|")),
2086 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
2087 relation.0.green().into(),
2088 ]
2089 },
2090 )
2091 };
2092
2093 let new_root = SyntaxNode::new_root_mut(
2094 self.0.replace_with(
2095 self.0
2096 .green()
2097 .splice_children(position..position, new_children),
2098 ),
2099 );
2100
2101 if let Some(parent) = self.0.parent() {
2102 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2103 self.0 = parent
2104 .children_with_tokens()
2105 .nth(self.0.index())
2106 .unwrap()
2107 .clone()
2108 .into_node()
2109 .unwrap();
2110 } else {
2111 self.0 = new_root;
2112 }
2113 }
2114
2115 pub fn is_implied_by(&self, outer: &Entry) -> bool {
2140 if self == outer {
2142 return true;
2143 }
2144
2145 for inner_rel in self.relations() {
2147 for outer_rel in outer.relations() {
2148 if inner_rel.is_implied_by(&outer_rel) {
2149 return true;
2150 }
2151 }
2152 }
2153
2154 false
2155 }
2156}
2157
2158fn inject(builder: &mut GreenNodeBuilder, node: SyntaxNode) {
2159 builder.start_node(node.kind().into());
2160 for child in node.children_with_tokens() {
2161 match child {
2162 rowan::NodeOrToken::Node(child) => {
2163 inject(builder, child);
2164 }
2165 rowan::NodeOrToken::Token(token) => {
2166 builder.token(token.kind().into(), token.text());
2167 }
2168 }
2169 }
2170 builder.finish_node();
2171}
2172
2173impl From<Vec<Relation>> for Entry {
2174 fn from(relations: Vec<Relation>) -> Self {
2175 let mut builder = GreenNodeBuilder::new();
2176 builder.start_node(SyntaxKind::ENTRY.into());
2177 for (i, relation) in relations.into_iter().enumerate() {
2178 if i > 0 {
2179 builder.token(WHITESPACE.into(), " ");
2180 builder.token(COMMA.into(), "|");
2181 builder.token(WHITESPACE.into(), " ");
2182 }
2183 inject(&mut builder, relation.0);
2184 }
2185 builder.finish_node();
2186 Entry(SyntaxNode::new_root_mut(builder.finish()))
2187 }
2188}
2189
2190impl From<Relation> for Entry {
2191 fn from(relation: Relation) -> Self {
2192 Self::from(vec![relation])
2193 }
2194}
2195
2196fn tokenize_version(builder: &mut GreenNodeBuilder, version: &Version) {
2199 let version_str = version.to_string();
2200
2201 if let Some(colon_pos) = version_str.find(':') {
2203 builder.token(IDENT.into(), &version_str[..colon_pos]);
2205 builder.token(COLON.into(), ":");
2206 builder.token(IDENT.into(), &version_str[colon_pos + 1..]);
2208 } else {
2209 builder.token(IDENT.into(), version_str.as_str());
2211 }
2212}
2213
2214impl Relation {
2215 pub fn new(name: &str, version_constraint: Option<(VersionConstraint, Version)>) -> Self {
2229 let mut builder = GreenNodeBuilder::new();
2230 builder.start_node(SyntaxKind::RELATION.into());
2231 builder.token(IDENT.into(), name);
2232 if let Some((vc, version)) = version_constraint {
2233 builder.token(WHITESPACE.into(), " ");
2234 builder.start_node(SyntaxKind::VERSION.into());
2235 builder.token(L_PARENS.into(), "(");
2236 builder.start_node(SyntaxKind::CONSTRAINT.into());
2237 for c in vc.to_string().chars() {
2238 builder.token(
2239 match c {
2240 '>' => R_ANGLE.into(),
2241 '<' => L_ANGLE.into(),
2242 '=' => EQUAL.into(),
2243 _ => unreachable!(),
2244 },
2245 c.to_string().as_str(),
2246 );
2247 }
2248 builder.finish_node();
2249
2250 builder.token(WHITESPACE.into(), " ");
2251
2252 tokenize_version(&mut builder, &version);
2253
2254 builder.token(R_PARENS.into(), ")");
2255
2256 builder.finish_node();
2257 }
2258
2259 builder.finish_node();
2260 Relation(SyntaxNode::new_root_mut(builder.finish()))
2261 }
2262
2263 #[must_use]
2272 pub fn wrap_and_sort(&self) -> Self {
2273 let mut builder = GreenNodeBuilder::new();
2274 builder.start_node(SyntaxKind::RELATION.into());
2275 if let Some(name) = self.try_name() {
2276 builder.token(IDENT.into(), name.as_str());
2277 }
2278 if let Some(archqual) = self.archqual() {
2279 builder.token(COLON.into(), ":");
2280 builder.token(IDENT.into(), archqual.as_str());
2281 }
2282 if let Some((vc, version)) = self.version() {
2283 builder.token(WHITESPACE.into(), " ");
2284 builder.start_node(SyntaxKind::VERSION.into());
2285 builder.token(L_PARENS.into(), "(");
2286 builder.start_node(SyntaxKind::CONSTRAINT.into());
2287 builder.token(
2288 match vc {
2289 VersionConstraint::GreaterThanEqual => R_ANGLE.into(),
2290 VersionConstraint::LessThanEqual => L_ANGLE.into(),
2291 VersionConstraint::Equal => EQUAL.into(),
2292 VersionConstraint::GreaterThan => R_ANGLE.into(),
2293 VersionConstraint::LessThan => L_ANGLE.into(),
2294 },
2295 vc.to_string().as_str(),
2296 );
2297 builder.finish_node();
2298 builder.token(WHITESPACE.into(), " ");
2299 tokenize_version(&mut builder, &version);
2300 builder.token(R_PARENS.into(), ")");
2301 builder.finish_node();
2302 }
2303 if let Some(architectures) = self.architectures() {
2304 builder.token(WHITESPACE.into(), " ");
2305 builder.start_node(ARCHITECTURES.into());
2306 builder.token(L_BRACKET.into(), "[");
2307 for (i, arch) in architectures.enumerate() {
2308 if i > 0 {
2309 builder.token(WHITESPACE.into(), " ");
2310 }
2311 builder.token(IDENT.into(), arch.as_str());
2312 }
2313 builder.token(R_BRACKET.into(), "]");
2314 builder.finish_node();
2315 }
2316 for profiles in self.profiles() {
2317 builder.token(WHITESPACE.into(), " ");
2318 builder.start_node(PROFILES.into());
2319 builder.token(L_ANGLE.into(), "<");
2320 for (i, profile) in profiles.into_iter().enumerate() {
2321 if i > 0 {
2322 builder.token(WHITESPACE.into(), " ");
2323 }
2324 match profile {
2325 BuildProfile::Disabled(name) => {
2326 builder.token(NOT.into(), "!");
2327 builder.token(IDENT.into(), name.as_str());
2328 }
2329 BuildProfile::Enabled(name) => {
2330 builder.token(IDENT.into(), name.as_str());
2331 }
2332 }
2333 }
2334 builder.token(R_ANGLE.into(), ">");
2335 builder.finish_node();
2336 }
2337 builder.finish_node();
2338 Relation(SyntaxNode::new_root_mut(builder.finish()))
2339 }
2340
2341 pub fn simple(name: &str) -> Self {
2350 Self::new(name, None)
2351 }
2352
2353 pub fn drop_constraint(&mut self) -> bool {
2364 let version_token = self.0.children().find(|n| n.kind() == VERSION);
2365 if let Some(version_token) = version_token {
2366 while let Some(prev) = version_token.prev_sibling_or_token() {
2368 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
2369 prev.detach();
2370 } else {
2371 break;
2372 }
2373 }
2374 version_token.detach();
2375 return true;
2376 }
2377
2378 false
2379 }
2380
2381 pub fn try_name(&self) -> Option<String> {
2393 self.0
2394 .children_with_tokens()
2395 .find_map(|it| match it {
2396 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2397 _ => None,
2398 })
2399 .map(|token| token.text().to_string())
2400 }
2401
2402 #[deprecated(
2415 since = "0.3.6",
2416 note = "Use try_name() instead, which returns Option<String>"
2417 )]
2418 pub fn name(&self) -> String {
2419 self.try_name().expect("Relation has no package name")
2420 }
2421
2422 pub fn archqual(&self) -> Option<String> {
2431 let archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
2432 let node = if let Some(archqual) = archqual {
2433 archqual.children_with_tokens().find_map(|it| match it {
2434 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2435 _ => None,
2436 })
2437 } else {
2438 None
2439 };
2440 node.map(|n| n.text().to_string())
2441 }
2442
2443 pub fn set_archqual(&mut self, archqual: &str) {
2453 let mut builder = GreenNodeBuilder::new();
2454 builder.start_node(ARCHQUAL.into());
2455 builder.token(COLON.into(), ":");
2456 builder.token(IDENT.into(), archqual);
2457 builder.finish_node();
2458
2459 let node_archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
2460 if let Some(node_archqual) = node_archqual {
2461 self.0.splice_children(
2462 node_archqual.index()..node_archqual.index() + 1,
2463 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2464 );
2465 } else {
2466 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
2467 let idx = if let Some(name_node) = name_node {
2468 name_node.index() + 1
2469 } else {
2470 0
2471 };
2472 self.0.splice_children(
2473 idx..idx,
2474 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2475 );
2476 }
2477 }
2478
2479 pub fn version(&self) -> Option<(VersionConstraint, Version)> {
2481 let vc = self.0.children().find(|n| n.kind() == VERSION);
2482 let vc = vc.as_ref()?;
2483 let constraint = vc.children().find(|n| n.kind() == CONSTRAINT);
2484
2485 let version_str: String = vc
2487 .children_with_tokens()
2488 .filter_map(|it| match it {
2489 SyntaxElement::Token(token) if token.kind() == IDENT || token.kind() == COLON => {
2490 Some(token.text().to_string())
2491 }
2492 _ => None,
2493 })
2494 .collect();
2495
2496 if let Some(constraint) = constraint {
2497 if !version_str.is_empty() {
2498 let vc: VersionConstraint = constraint.to_string().parse().unwrap();
2499 Some((vc, version_str.parse().unwrap()))
2500 } else {
2501 None
2502 }
2503 } else {
2504 None
2505 }
2506 }
2507
2508 pub fn set_version(&mut self, version_constraint: Option<(VersionConstraint, Version)>) {
2519 let current_version = self.0.children().find(|n| n.kind() == VERSION);
2520 if let Some((vc, version)) = version_constraint {
2521 let mut builder = GreenNodeBuilder::new();
2522 builder.start_node(VERSION.into());
2523 builder.token(L_PARENS.into(), "(");
2524 builder.start_node(CONSTRAINT.into());
2525 match vc {
2526 VersionConstraint::GreaterThanEqual => {
2527 builder.token(R_ANGLE.into(), ">");
2528 builder.token(EQUAL.into(), "=");
2529 }
2530 VersionConstraint::LessThanEqual => {
2531 builder.token(L_ANGLE.into(), "<");
2532 builder.token(EQUAL.into(), "=");
2533 }
2534 VersionConstraint::Equal => {
2535 builder.token(EQUAL.into(), "=");
2536 }
2537 VersionConstraint::GreaterThan => {
2538 builder.token(R_ANGLE.into(), ">");
2539 }
2540 VersionConstraint::LessThan => {
2541 builder.token(L_ANGLE.into(), "<");
2542 }
2543 }
2544 builder.finish_node(); builder.token(WHITESPACE.into(), " ");
2546 tokenize_version(&mut builder, &version);
2547 builder.token(R_PARENS.into(), ")");
2548 builder.finish_node(); if let Some(current_version) = current_version {
2551 self.0.splice_children(
2552 current_version.index()..current_version.index() + 1,
2553 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2554 );
2555 } else {
2556 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
2557 let idx = if let Some(name_node) = name_node {
2558 name_node.index() + 1
2559 } else {
2560 0
2561 };
2562 let new_children = vec![
2563 GreenToken::new(WHITESPACE.into(), " ").into(),
2564 builder.finish().into(),
2565 ];
2566 let new_root = SyntaxNode::new_root_mut(
2567 self.0.green().splice_children(idx..idx, new_children),
2568 );
2569 if let Some(parent) = self.0.parent() {
2570 parent
2571 .splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2572 self.0 = parent
2573 .children_with_tokens()
2574 .nth(self.0.index())
2575 .unwrap()
2576 .clone()
2577 .into_node()
2578 .unwrap();
2579 } else {
2580 self.0 = new_root;
2581 }
2582 }
2583 } else if let Some(current_version) = current_version {
2584 while let Some(prev) = current_version.prev_sibling_or_token() {
2586 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
2587 prev.detach();
2588 } else {
2589 break;
2590 }
2591 }
2592 current_version.detach();
2593 }
2594 }
2595
2596 pub fn architectures(&self) -> Option<impl Iterator<Item = String> + '_> {
2605 let architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES)?;
2606
2607 Some(architectures.children_with_tokens().filter_map(|node| {
2608 let token = node.as_token()?;
2609 if token.kind() == IDENT {
2610 Some(token.text().to_string())
2611 } else {
2612 None
2613 }
2614 }))
2615 }
2616
2617 pub fn profiles(&self) -> impl Iterator<Item = Vec<BuildProfile>> + '_ {
2627 let profiles = self.0.children().filter(|n| n.kind() == PROFILES);
2628
2629 profiles.map(|profile| {
2630 let mut ret = vec![];
2632 let mut current = vec![];
2633 for token in profile.children_with_tokens() {
2634 match token.kind() {
2635 WHITESPACE | NEWLINE => {
2636 if !current.is_empty() {
2637 ret.push(current.join("").parse::<BuildProfile>().unwrap());
2638 current = vec![];
2639 }
2640 }
2641 L_ANGLE | R_ANGLE => {}
2642 _ => {
2643 current.push(token.to_string());
2644 }
2645 }
2646 }
2647 if !current.is_empty() {
2648 ret.push(current.concat().parse().unwrap());
2649 }
2650 ret
2651 })
2652 }
2653
2654 pub fn remove(&mut self) {
2665 let is_first = !self
2666 .0
2667 .siblings(Direction::Prev)
2668 .skip(1)
2669 .any(|n| n.kind() == RELATION);
2670 if !is_first {
2671 while let Some(n) = self.0.prev_sibling_or_token() {
2674 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
2675 n.detach();
2676 } else if n.kind() == PIPE {
2677 n.detach();
2678 break;
2679 } else {
2680 break;
2681 }
2682 }
2683 while let Some(n) = self.0.prev_sibling_or_token() {
2684 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
2685 n.detach();
2686 } else {
2687 break;
2688 }
2689 }
2690 } else {
2691 while let Some(n) = self.0.next_sibling_or_token() {
2694 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
2695 n.detach();
2696 } else if n.kind() == PIPE {
2697 n.detach();
2698 break;
2699 } else {
2700 break;
2701 }
2702 }
2703
2704 while let Some(n) = self.0.next_sibling_or_token() {
2705 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
2706 n.detach();
2707 } else {
2708 break;
2709 }
2710 }
2711 }
2712 if let Some(mut parent) = self.0.parent().and_then(Entry::cast) {
2714 if parent.is_empty() {
2715 parent.remove();
2716 } else {
2717 self.0.detach();
2718 }
2719 } else {
2720 self.0.detach();
2721 }
2722 }
2723
2724 pub fn set_architectures<'a>(&mut self, architectures: impl Iterator<Item = &'a str>) {
2734 let mut builder = GreenNodeBuilder::new();
2735 builder.start_node(ARCHITECTURES.into());
2736 builder.token(L_BRACKET.into(), "[");
2737 for (i, arch) in architectures.enumerate() {
2738 if i > 0 {
2739 builder.token(WHITESPACE.into(), " ");
2740 }
2741 builder.token(IDENT.into(), arch);
2742 }
2743 builder.token(R_BRACKET.into(), "]");
2744 builder.finish_node();
2745
2746 let node_architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES);
2747 if let Some(node_architectures) = node_architectures {
2748 let new_root = SyntaxNode::new_root_mut(builder.finish());
2749 self.0.splice_children(
2750 node_architectures.index()..node_architectures.index() + 1,
2751 vec![new_root.into()],
2752 );
2753 } else {
2754 let profiles = self.0.children().find(|n| n.kind() == PROFILES);
2755 let idx = if let Some(profiles) = profiles {
2756 profiles.index()
2757 } else {
2758 self.0.children_with_tokens().count()
2759 };
2760 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
2761 idx..idx,
2762 vec![
2763 GreenToken::new(WHITESPACE.into(), " ").into(),
2764 builder.finish().into(),
2765 ],
2766 ));
2767 if let Some(parent) = self.0.parent() {
2768 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2769 self.0 = parent
2770 .children_with_tokens()
2771 .nth(self.0.index())
2772 .unwrap()
2773 .clone()
2774 .into_node()
2775 .unwrap();
2776 } else {
2777 self.0 = new_root;
2778 }
2779 }
2780 }
2781
2782 pub fn add_profile(&mut self, profile: &[BuildProfile]) {
2793 let mut builder = GreenNodeBuilder::new();
2794 builder.start_node(PROFILES.into());
2795 builder.token(L_ANGLE.into(), "<");
2796 for (i, profile) in profile.iter().enumerate() {
2797 if i > 0 {
2798 builder.token(WHITESPACE.into(), " ");
2799 }
2800 match profile {
2801 BuildProfile::Disabled(name) => {
2802 builder.token(NOT.into(), "!");
2803 builder.token(IDENT.into(), name.as_str());
2804 }
2805 BuildProfile::Enabled(name) => {
2806 builder.token(IDENT.into(), name.as_str());
2807 }
2808 }
2809 }
2810 builder.token(R_ANGLE.into(), ">");
2811 builder.finish_node();
2812
2813 let node_profiles = self.0.children().find(|n| n.kind() == PROFILES);
2814 if let Some(node_profiles) = node_profiles {
2815 let new_root = SyntaxNode::new_root_mut(builder.finish());
2816 self.0.splice_children(
2817 node_profiles.index()..node_profiles.index() + 1,
2818 vec![new_root.into()],
2819 );
2820 } else {
2821 let idx = self.0.children_with_tokens().count();
2822 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
2823 idx..idx,
2824 vec![
2825 GreenToken::new(WHITESPACE.into(), " ").into(),
2826 builder.finish().into(),
2827 ],
2828 ));
2829 if let Some(parent) = self.0.parent() {
2830 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2831 self.0 = parent
2832 .children_with_tokens()
2833 .nth(self.0.index())
2834 .unwrap()
2835 .clone()
2836 .into_node()
2837 .unwrap();
2838 } else {
2839 self.0 = new_root;
2840 }
2841 }
2842 }
2843
2844 pub fn build(name: &str) -> RelationBuilder {
2846 RelationBuilder::new(name)
2847 }
2848
2849 pub fn is_implied_by(&self, outer: &Relation) -> bool {
2876 if self.try_name() != outer.try_name() {
2877 return false;
2878 }
2879
2880 let inner_version = self.version();
2881 let outer_version = outer.version();
2882
2883 if inner_version.is_none() {
2885 return true;
2886 }
2887
2888 if inner_version == outer_version {
2890 return true;
2891 }
2892
2893 if outer_version.is_none() {
2895 return false;
2896 }
2897
2898 let (inner_constraint, inner_ver) = inner_version.unwrap();
2899 let (outer_constraint, outer_ver) = outer_version.unwrap();
2900
2901 use VersionConstraint::*;
2902 match inner_constraint {
2903 GreaterThanEqual => match outer_constraint {
2904 GreaterThan => outer_ver > inner_ver,
2905 GreaterThanEqual | Equal => outer_ver >= inner_ver,
2906 LessThan | LessThanEqual => false,
2907 },
2908 Equal => match outer_constraint {
2909 Equal => outer_ver == inner_ver,
2910 _ => false,
2911 },
2912 LessThan => match outer_constraint {
2913 LessThan => outer_ver <= inner_ver,
2914 LessThanEqual | Equal => outer_ver < inner_ver,
2915 GreaterThan | GreaterThanEqual => false,
2916 },
2917 LessThanEqual => match outer_constraint {
2918 LessThanEqual | Equal | LessThan => outer_ver <= inner_ver,
2919 GreaterThan | GreaterThanEqual => false,
2920 },
2921 GreaterThan => match outer_constraint {
2922 GreaterThan => outer_ver >= inner_ver,
2923 Equal | GreaterThanEqual => outer_ver > inner_ver,
2924 LessThan | LessThanEqual => false,
2925 },
2926 }
2927 }
2928}
2929
2930pub struct RelationBuilder {
2944 name: String,
2945 version_constraint: Option<(VersionConstraint, Version)>,
2946 archqual: Option<String>,
2947 architectures: Option<Vec<String>>,
2948 profiles: Vec<Vec<BuildProfile>>,
2949}
2950
2951impl RelationBuilder {
2952 fn new(name: &str) -> Self {
2954 Self {
2955 name: name.to_string(),
2956 version_constraint: None,
2957 archqual: None,
2958 architectures: None,
2959 profiles: vec![],
2960 }
2961 }
2962
2963 pub fn version_constraint(mut self, vc: VersionConstraint, version: Version) -> Self {
2965 self.version_constraint = Some((vc, version));
2966 self
2967 }
2968
2969 pub fn archqual(mut self, archqual: &str) -> Self {
2971 self.archqual = Some(archqual.to_string());
2972 self
2973 }
2974
2975 pub fn architectures(mut self, architectures: Vec<String>) -> Self {
2977 self.architectures = Some(architectures);
2978 self
2979 }
2980
2981 pub fn profiles(mut self, profiles: Vec<Vec<BuildProfile>>) -> Self {
2983 self.profiles = profiles;
2984 self
2985 }
2986
2987 pub fn add_profile(mut self, profile: Vec<BuildProfile>) -> Self {
2989 self.profiles.push(profile);
2990 self
2991 }
2992
2993 pub fn build(self) -> Relation {
2995 let mut relation = Relation::new(&self.name, self.version_constraint);
2996 if let Some(archqual) = &self.archqual {
2997 relation.set_archqual(archqual);
2998 }
2999 if let Some(architectures) = &self.architectures {
3000 relation.set_architectures(architectures.iter().map(|s| s.as_str()));
3001 }
3002 for profile in &self.profiles {
3003 relation.add_profile(profile);
3004 }
3005 relation
3006 }
3007}
3008
3009impl PartialOrd for Relation {
3010 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
3011 Some(self.cmp(other))
3012 }
3013}
3014
3015impl Eq for Relation {}
3016
3017impl Ord for Relation {
3018 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
3019 let name_cmp = self.try_name().cmp(&other.try_name());
3021 if name_cmp != std::cmp::Ordering::Equal {
3022 return name_cmp;
3023 }
3024
3025 let self_version = self.version();
3026 let other_version = other.version();
3027
3028 match (self_version, other_version) {
3029 (Some((self_vc, self_version)), Some((other_vc, other_version))) => {
3030 let vc_cmp = self_vc.cmp(&other_vc);
3031 if vc_cmp != std::cmp::Ordering::Equal {
3032 return vc_cmp;
3033 }
3034
3035 self_version.cmp(&other_version)
3036 }
3037 (Some(_), None) => std::cmp::Ordering::Greater,
3038 (None, Some(_)) => std::cmp::Ordering::Less,
3039 (None, None) => std::cmp::Ordering::Equal,
3040 }
3041 }
3042}
3043
3044impl std::str::FromStr for Relations {
3045 type Err = String;
3046
3047 fn from_str(s: &str) -> Result<Self, Self::Err> {
3048 let parse = parse(s, false);
3049 if parse.errors.is_empty() {
3050 Ok(parse.root_mut())
3051 } else {
3052 Err(parse.errors.join("\n"))
3053 }
3054 }
3055}
3056
3057impl std::str::FromStr for Entry {
3058 type Err = String;
3059
3060 fn from_str(s: &str) -> Result<Self, Self::Err> {
3061 let root: Relations = s.parse()?;
3062
3063 let mut entries = root.entries();
3064 let entry = if let Some(entry) = entries.next() {
3065 entry
3066 } else {
3067 return Err("No entry found".to_string());
3068 };
3069
3070 if entries.next().is_some() {
3071 return Err("Multiple entries found".to_string());
3072 }
3073
3074 Ok(entry)
3075 }
3076}
3077
3078impl std::str::FromStr for Relation {
3079 type Err = String;
3080
3081 fn from_str(s: &str) -> Result<Self, Self::Err> {
3082 let entry: Entry = s.parse()?;
3083
3084 let mut relations = entry.relations();
3085 let relation = if let Some(relation) = relations.next() {
3086 relation
3087 } else {
3088 return Err("No relation found".to_string());
3089 };
3090
3091 if relations.next().is_some() {
3092 return Err("Multiple relations found".to_string());
3093 }
3094
3095 Ok(relation)
3096 }
3097}
3098
3099impl From<crate::lossy::Relation> for Relation {
3100 fn from(relation: crate::lossy::Relation) -> Self {
3101 let mut builder = Relation::build(&relation.name);
3102
3103 if let Some((vc, version)) = relation.version {
3104 builder = builder.version_constraint(vc, version);
3105 }
3106
3107 if let Some(archqual) = relation.archqual {
3108 builder = builder.archqual(&archqual);
3109 }
3110
3111 if let Some(architectures) = relation.architectures {
3112 builder = builder.architectures(architectures);
3113 }
3114
3115 builder = builder.profiles(relation.profiles);
3116
3117 builder.build()
3118 }
3119}
3120
3121impl From<Relation> for crate::lossy::Relation {
3122 fn from(relation: Relation) -> Self {
3123 crate::lossy::Relation {
3124 name: relation.try_name().unwrap_or_default(),
3125 version: relation.version(),
3126 archqual: relation.archqual(),
3127 architectures: relation.architectures().map(|a| a.collect()),
3128 profiles: relation.profiles().collect(),
3129 }
3130 }
3131}
3132
3133impl From<Entry> for Vec<crate::lossy::Relation> {
3134 fn from(entry: Entry) -> Self {
3135 entry.relations().map(|r| r.into()).collect()
3136 }
3137}
3138
3139impl From<Vec<crate::lossy::Relation>> for Entry {
3140 fn from(relations: Vec<crate::lossy::Relation>) -> Self {
3141 let relations: Vec<Relation> = relations.into_iter().map(|r| r.into()).collect();
3142 Entry::from(relations)
3143 }
3144}
3145
3146#[cfg(test)]
3147mod tests {
3148 use super::*;
3149
3150 #[test]
3151 fn test_parse() {
3152 let input = "python3-dulwich";
3153 let parsed: Relations = input.parse().unwrap();
3154 assert_eq!(parsed.to_string(), input);
3155 assert_eq!(parsed.entries().count(), 1);
3156 let entry = parsed.entries().next().unwrap();
3157 assert_eq!(entry.to_string(), "python3-dulwich");
3158 assert_eq!(entry.relations().count(), 1);
3159 let relation = entry.relations().next().unwrap();
3160 assert_eq!(relation.to_string(), "python3-dulwich");
3161 assert_eq!(relation.version(), None);
3162
3163 let input = "python3-dulwich (>= 0.20.21)";
3164 let parsed: Relations = input.parse().unwrap();
3165 assert_eq!(parsed.to_string(), input);
3166 assert_eq!(parsed.entries().count(), 1);
3167 let entry = parsed.entries().next().unwrap();
3168 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3169 assert_eq!(entry.relations().count(), 1);
3170 let relation = entry.relations().next().unwrap();
3171 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
3172 assert_eq!(
3173 relation.version(),
3174 Some((
3175 VersionConstraint::GreaterThanEqual,
3176 "0.20.21".parse().unwrap()
3177 ))
3178 );
3179 }
3180
3181 #[test]
3182 fn test_multiple() {
3183 let input = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)";
3184 let parsed: Relations = input.parse().unwrap();
3185 assert_eq!(parsed.to_string(), input);
3186 assert_eq!(parsed.entries().count(), 2);
3187 let entry = parsed.entries().next().unwrap();
3188 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3189 assert_eq!(entry.relations().count(), 1);
3190 let relation = entry.relations().next().unwrap();
3191 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
3192 assert_eq!(
3193 relation.version(),
3194 Some((
3195 VersionConstraint::GreaterThanEqual,
3196 "0.20.21".parse().unwrap()
3197 ))
3198 );
3199 let entry = parsed.entries().nth(1).unwrap();
3200 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.21)");
3201 assert_eq!(entry.relations().count(), 1);
3202 let relation = entry.relations().next().unwrap();
3203 assert_eq!(relation.to_string(), "python3-dulwich (<< 0.21)");
3204 assert_eq!(
3205 relation.version(),
3206 Some((VersionConstraint::LessThan, "0.21".parse().unwrap()))
3207 );
3208 }
3209
3210 #[test]
3211 fn test_architectures() {
3212 let input = "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]";
3213 let parsed: Relations = input.parse().unwrap();
3214 assert_eq!(parsed.to_string(), input);
3215 assert_eq!(parsed.entries().count(), 1);
3216 let entry = parsed.entries().next().unwrap();
3217 assert_eq!(
3218 entry.to_string(),
3219 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
3220 );
3221 assert_eq!(entry.relations().count(), 1);
3222 let relation = entry.relations().next().unwrap();
3223 assert_eq!(
3224 relation.to_string(),
3225 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
3226 );
3227 assert_eq!(relation.version(), None);
3228 assert_eq!(
3229 relation.architectures().unwrap().collect::<Vec<_>>(),
3230 vec![
3231 "amd64", "arm64", "armhf", "i386", "mips", "mips64el", "mipsel", "ppc64el", "s390x"
3232 ]
3233 .into_iter()
3234 .map(|s| s.to_string())
3235 .collect::<Vec<_>>()
3236 );
3237 }
3238
3239 #[test]
3240 fn test_profiles() {
3241 let input = "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>, bar";
3242 let parsed: Relations = input.parse().unwrap();
3243 assert_eq!(parsed.to_string(), input);
3244 assert_eq!(parsed.entries().count(), 2);
3245 let entry = parsed.entries().next().unwrap();
3246 assert_eq!(
3247 entry.to_string(),
3248 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
3249 );
3250 assert_eq!(entry.relations().count(), 1);
3251 let relation = entry.relations().next().unwrap();
3252 assert_eq!(
3253 relation.to_string(),
3254 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
3255 );
3256 assert_eq!(
3257 relation.version(),
3258 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap()))
3259 );
3260 assert_eq!(
3261 relation.architectures().unwrap().collect::<Vec<_>>(),
3262 vec!["i386", "arm"]
3263 .into_iter()
3264 .map(|s| s.to_string())
3265 .collect::<Vec<_>>()
3266 );
3267 assert_eq!(
3268 relation.profiles().collect::<Vec<_>>(),
3269 vec![
3270 vec![BuildProfile::Disabled("nocheck".to_string())],
3271 vec![BuildProfile::Disabled("cross".to_string())]
3272 ]
3273 );
3274 }
3275
3276 #[test]
3277 fn test_substvar() {
3278 let input = "${shlibs:Depends}";
3279
3280 let (parsed, errors) = Relations::parse_relaxed(input, true);
3281 assert_eq!(errors, Vec::<String>::new());
3282 assert_eq!(parsed.to_string(), input);
3283 assert_eq!(parsed.entries().count(), 0);
3284
3285 assert_eq!(
3286 parsed.substvars().collect::<Vec<_>>(),
3287 vec!["${shlibs:Depends}"]
3288 );
3289 }
3290
3291 #[test]
3292 fn test_new() {
3293 let r = Relation::new(
3294 "samba",
3295 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
3296 );
3297
3298 assert_eq!(r.to_string(), "samba (>= 2.0)");
3299 }
3300
3301 #[test]
3302 fn test_drop_constraint() {
3303 let mut r = Relation::new(
3304 "samba",
3305 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
3306 );
3307
3308 r.drop_constraint();
3309
3310 assert_eq!(r.to_string(), "samba");
3311 }
3312
3313 #[test]
3314 fn test_simple() {
3315 let r = Relation::simple("samba");
3316
3317 assert_eq!(r.to_string(), "samba");
3318 }
3319
3320 #[test]
3321 fn test_remove_first_entry() {
3322 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3323 .parse()
3324 .unwrap();
3325 let removed = rels.remove_entry(0);
3326 assert_eq!(removed.to_string(), "python3-dulwich (>= 0.20.21)");
3327 assert_eq!(rels.to_string(), "python3-dulwich (<< 0.21)");
3328 }
3329
3330 #[test]
3331 fn test_remove_last_entry() {
3332 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3333 .parse()
3334 .unwrap();
3335 rels.remove_entry(1);
3336 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
3337 }
3338
3339 #[test]
3340 fn test_remove_middle() {
3341 let mut rels: Relations =
3342 r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21), python3-dulwich (<< 0.22)"#
3343 .parse()
3344 .unwrap();
3345 rels.remove_entry(1);
3346 assert_eq!(
3347 rels.to_string(),
3348 "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.22)"
3349 );
3350 }
3351
3352 #[test]
3353 fn test_remove_added() {
3354 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
3355 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3356 rels.push(entry);
3357 rels.remove_entry(1);
3358 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
3359 }
3360
3361 #[test]
3362 fn test_push() {
3363 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
3364 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3365 rels.push(entry);
3366 assert_eq!(
3367 rels.to_string(),
3368 "python3-dulwich (>= 0.20.21), python3-dulwich"
3369 );
3370 }
3371
3372 #[test]
3373 fn test_insert_with_custom_separator() {
3374 let mut rels: Relations = "python3".parse().unwrap();
3375 let entry = Entry::from(vec![Relation::simple("debhelper")]);
3376 rels.insert_with_separator(1, entry, Some("\n "));
3377 assert_eq!(rels.to_string(), "python3,\n debhelper");
3378 }
3379
3380 #[test]
3381 fn test_insert_with_wrap_and_sort_separator() {
3382 let mut rels: Relations = "python3".parse().unwrap();
3383 let entry = Entry::from(vec![Relation::simple("rustc")]);
3384 rels.insert_with_separator(1, entry, Some("\n "));
3386 assert_eq!(rels.to_string(), "python3,\n rustc");
3387 }
3388
3389 #[test]
3390 fn test_push_from_empty() {
3391 let mut rels: Relations = "".parse().unwrap();
3392 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3393 rels.push(entry);
3394 assert_eq!(rels.to_string(), "python3-dulwich");
3395 }
3396
3397 #[test]
3398 fn test_insert() {
3399 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3400 .parse()
3401 .unwrap();
3402 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3403 rels.insert(1, entry);
3404 assert_eq!(
3405 rels.to_string(),
3406 "python3-dulwich (>= 0.20.21), python3-dulwich, python3-dulwich (<< 0.21)"
3407 );
3408 }
3409
3410 #[test]
3411 fn test_insert_at_start() {
3412 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3413 .parse()
3414 .unwrap();
3415 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3416 rels.insert(0, entry);
3417 assert_eq!(
3418 rels.to_string(),
3419 "python3-dulwich, python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3420 );
3421 }
3422
3423 #[test]
3424 fn test_insert_after_error() {
3425 let (mut rels, errors) = Relations::parse_relaxed("@foo@, debhelper (>= 1.0)", false);
3426 assert_eq!(
3427 errors,
3428 vec![
3429 "expected $ or identifier but got ERROR",
3430 "expected comma or end of file but got Some(IDENT)",
3431 "expected $ or identifier but got ERROR"
3432 ]
3433 );
3434 let entry = Entry::from(vec![Relation::simple("bar")]);
3435 rels.push(entry);
3436 assert_eq!(rels.to_string(), "@foo@, debhelper (>= 1.0), bar");
3437 }
3438
3439 #[test]
3440 fn test_insert_before_error() {
3441 let (mut rels, errors) = Relations::parse_relaxed("debhelper (>= 1.0), @foo@, bla", false);
3442 assert_eq!(
3443 errors,
3444 vec![
3445 "expected $ or identifier but got ERROR",
3446 "expected comma or end of file but got Some(IDENT)",
3447 "expected $ or identifier but got ERROR"
3448 ]
3449 );
3450 let entry = Entry::from(vec![Relation::simple("bar")]);
3451 rels.insert(0, entry);
3452 assert_eq!(rels.to_string(), "bar, debhelper (>= 1.0), @foo@, bla");
3453 }
3454
3455 #[test]
3456 fn test_replace() {
3457 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3458 .parse()
3459 .unwrap();
3460 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3461 rels.replace(1, entry);
3462 assert_eq!(
3463 rels.to_string(),
3464 "python3-dulwich (>= 0.20.21), python3-dulwich"
3465 );
3466 }
3467
3468 #[test]
3469 fn test_relation_from_entries() {
3470 let entries = vec![
3471 Entry::from(vec![Relation::simple("python3-dulwich")]),
3472 Entry::from(vec![Relation::simple("python3-breezy")]),
3473 ];
3474 let rels: Relations = entries.into();
3475 assert_eq!(rels.entries().count(), 2);
3476 assert_eq!(rels.to_string(), "python3-dulwich, python3-breezy");
3477 }
3478
3479 #[test]
3480 fn test_entry_from_relations() {
3481 let relations = vec![
3482 Relation::simple("python3-dulwich"),
3483 Relation::simple("python3-breezy"),
3484 ];
3485 let entry: Entry = relations.into();
3486 assert_eq!(entry.relations().count(), 2);
3487 assert_eq!(entry.to_string(), "python3-dulwich | python3-breezy");
3488 }
3489
3490 #[test]
3491 fn test_parse_entry() {
3492 let parsed: Entry = "python3-dulwich (>= 0.20.21) | bar".parse().unwrap();
3493 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21) | bar");
3494 assert_eq!(parsed.relations().count(), 2);
3495
3496 assert_eq!(
3497 "foo, bar".parse::<Entry>().unwrap_err(),
3498 "Multiple entries found"
3499 );
3500 assert_eq!("".parse::<Entry>().unwrap_err(), "No entry found");
3501 }
3502
3503 #[test]
3504 fn test_parse_relation() {
3505 let parsed: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3506 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21)");
3507 assert_eq!(
3508 parsed.version(),
3509 Some((
3510 VersionConstraint::GreaterThanEqual,
3511 "0.20.21".parse().unwrap()
3512 ))
3513 );
3514 assert_eq!(
3515 "foo | bar".parse::<Relation>().unwrap_err(),
3516 "Multiple relations found"
3517 );
3518 assert_eq!("".parse::<Relation>().unwrap_err(), "No entry found");
3519 }
3520
3521 #[test]
3522 fn test_special() {
3523 let parsed: Relation = "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
3524 .parse()
3525 .unwrap();
3526 assert_eq!(
3527 parsed.to_string(),
3528 "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
3529 );
3530 assert_eq!(
3531 parsed.version(),
3532 Some((
3533 VersionConstraint::GreaterThanEqual,
3534 "0.1.138-~~".parse().unwrap()
3535 ))
3536 );
3537 assert_eq!(parsed.archqual(), Some("amd64".to_string()));
3538 assert_eq!(parsed.name(), "librust-breezyshim+dirty-tracker-dev");
3539 }
3540
3541 #[test]
3542 fn test_relations_satisfied_by() {
3543 let rels: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3544 .parse()
3545 .unwrap();
3546 let satisfied = |name: &str| -> Option<debversion::Version> {
3547 match name {
3548 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
3549 _ => None,
3550 }
3551 };
3552 assert!(rels.satisfied_by(satisfied));
3553
3554 let satisfied = |name: &str| match name {
3555 "python3-dulwich" => Some("0.21".parse().unwrap()),
3556 _ => None,
3557 };
3558 assert!(!rels.satisfied_by(satisfied));
3559
3560 let satisfied = |name: &str| match name {
3561 "python3-dulwich" => Some("0.20.20".parse().unwrap()),
3562 _ => None,
3563 };
3564 assert!(!rels.satisfied_by(satisfied));
3565 }
3566
3567 #[test]
3568 fn test_entry_satisfied_by() {
3569 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3570 .parse()
3571 .unwrap();
3572 let satisfied = |name: &str| -> Option<debversion::Version> {
3573 match name {
3574 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
3575 _ => None,
3576 }
3577 };
3578 assert!(entry.satisfied_by(satisfied));
3579 let satisfied = |name: &str| -> Option<debversion::Version> {
3580 match name {
3581 "python3-dulwich" => Some("0.18".parse().unwrap()),
3582 _ => None,
3583 }
3584 };
3585 assert!(!entry.satisfied_by(satisfied));
3586 }
3587
3588 #[test]
3589 fn test_wrap_and_sort_relation() {
3590 let relation: Relation = " python3-dulwich (>= 11) [ amd64 ] < lala>"
3591 .parse()
3592 .unwrap();
3593
3594 let wrapped = relation.wrap_and_sort();
3595
3596 assert_eq!(
3597 wrapped.to_string(),
3598 "python3-dulwich (>= 11) [amd64] <lala>"
3599 );
3600 }
3601
3602 #[test]
3603 fn test_wrap_and_sort_relations() {
3604 let entry: Relations =
3605 "python3-dulwich (>= 0.20.21) | bar, \n\n\n\npython3-dulwich (<< 0.21)"
3606 .parse()
3607 .unwrap();
3608
3609 let wrapped = entry.wrap_and_sort();
3610
3611 assert_eq!(
3612 wrapped.to_string(),
3613 "bar | python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3614 );
3615 }
3616
3617 #[cfg(feature = "serde")]
3618 #[test]
3619 fn test_serialize_relations() {
3620 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3621 .parse()
3622 .unwrap();
3623 let serialized = serde_json::to_string(&relations).unwrap();
3624 assert_eq!(
3625 serialized,
3626 r#""python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)""#
3627 );
3628 }
3629
3630 #[cfg(feature = "serde")]
3631 #[test]
3632 fn test_deserialize_relations() {
3633 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3634 .parse()
3635 .unwrap();
3636 let serialized = serde_json::to_string(&relations).unwrap();
3637 let deserialized: Relations = serde_json::from_str(&serialized).unwrap();
3638 assert_eq!(deserialized.to_string(), relations.to_string());
3639 }
3640
3641 #[cfg(feature = "serde")]
3642 #[test]
3643 fn test_serialize_relation() {
3644 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3645 let serialized = serde_json::to_string(&relation).unwrap();
3646 assert_eq!(serialized, r#""python3-dulwich (>= 0.20.21)""#);
3647 }
3648
3649 #[cfg(feature = "serde")]
3650 #[test]
3651 fn test_deserialize_relation() {
3652 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3653 let serialized = serde_json::to_string(&relation).unwrap();
3654 let deserialized: Relation = serde_json::from_str(&serialized).unwrap();
3655 assert_eq!(deserialized.to_string(), relation.to_string());
3656 }
3657
3658 #[cfg(feature = "serde")]
3659 #[test]
3660 fn test_serialize_entry() {
3661 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3662 .parse()
3663 .unwrap();
3664 let serialized = serde_json::to_string(&entry).unwrap();
3665 assert_eq!(
3666 serialized,
3667 r#""python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)""#
3668 );
3669 }
3670
3671 #[cfg(feature = "serde")]
3672 #[test]
3673 fn test_deserialize_entry() {
3674 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3675 .parse()
3676 .unwrap();
3677 let serialized = serde_json::to_string(&entry).unwrap();
3678 let deserialized: Entry = serde_json::from_str(&serialized).unwrap();
3679 assert_eq!(deserialized.to_string(), entry.to_string());
3680 }
3681
3682 #[test]
3683 fn test_remove_first_relation() {
3684 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3685 .parse()
3686 .unwrap();
3687 let mut rel = entry.relations().next().unwrap();
3688 rel.remove();
3689 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.18)");
3690 }
3691
3692 #[test]
3693 fn test_remove_last_relation() {
3694 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3695 .parse()
3696 .unwrap();
3697 let mut rel = entry.relations().nth(1).unwrap();
3698 rel.remove();
3699 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3700 }
3701
3702 #[test]
3703 fn test_remove_only_relation() {
3704 let entry: Entry = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3705 let mut rel = entry.relations().next().unwrap();
3706 rel.remove();
3707 assert_eq!(entry.to_string(), "");
3708 }
3709
3710 #[test]
3711 fn test_relations_is_empty() {
3712 let entry: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3713 assert!(!entry.is_empty());
3714 assert_eq!(1, entry.len());
3715 let mut rel = entry.entries().next().unwrap();
3716 rel.remove();
3717 assert!(entry.is_empty());
3718 assert_eq!(0, entry.len());
3719 }
3720
3721 #[test]
3722 fn test_entry_is_empty() {
3723 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3724 .parse()
3725 .unwrap();
3726 assert!(!entry.is_empty());
3727 assert_eq!(2, entry.len());
3728 let mut rel = entry.relations().next().unwrap();
3729 rel.remove();
3730 assert!(!entry.is_empty());
3731 assert_eq!(1, entry.len());
3732 let mut rel = entry.relations().next().unwrap();
3733 rel.remove();
3734 assert!(entry.is_empty());
3735 assert_eq!(0, entry.len());
3736 }
3737
3738 #[test]
3739 fn test_relation_set_version() {
3740 let mut rel: Relation = "samba".parse().unwrap();
3741 rel.set_version(None);
3742 assert_eq!("samba", rel.to_string());
3743 rel.set_version(Some((
3744 VersionConstraint::GreaterThanEqual,
3745 "2.0".parse().unwrap(),
3746 )));
3747 assert_eq!("samba (>= 2.0)", rel.to_string());
3748 rel.set_version(None);
3749 assert_eq!("samba", rel.to_string());
3750 rel.set_version(Some((
3751 VersionConstraint::GreaterThanEqual,
3752 "2.0".parse().unwrap(),
3753 )));
3754 rel.set_version(Some((
3755 VersionConstraint::GreaterThanEqual,
3756 "1.1".parse().unwrap(),
3757 )));
3758 assert_eq!("samba (>= 1.1)", rel.to_string());
3759 }
3760
3761 #[test]
3762 fn test_replace_relation() {
3763 let mut entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3764 .parse()
3765 .unwrap();
3766 let new_rel = Relation::simple("python3-breezy");
3767 entry.replace(0, new_rel);
3768 assert_eq!(
3769 entry.to_string(),
3770 "python3-breezy | python3-dulwich (<< 0.18)"
3771 );
3772 }
3773
3774 #[test]
3775 fn test_entry_push_relation() {
3776 let relations: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3777 let new_rel = Relation::simple("python3-breezy");
3778 let mut entry = relations.entries().next().unwrap();
3779 entry.push(new_rel);
3780 assert_eq!(
3781 entry.to_string(),
3782 "python3-dulwich (>= 0.20.21) | python3-breezy"
3783 );
3784 assert_eq!(
3785 relations.to_string(),
3786 "python3-dulwich (>= 0.20.21) | python3-breezy"
3787 );
3788 }
3789
3790 #[test]
3791 fn test_relations_remove_empty_entry() {
3792 let (mut relations, errors) = Relations::parse_relaxed("foo, , bar, ", false);
3793 assert_eq!(errors, Vec::<String>::new());
3794 assert_eq!(relations.to_string(), "foo, , bar, ");
3795 assert_eq!(relations.len(), 2);
3796 assert_eq!(
3797 relations.entries().next().unwrap().to_string(),
3798 "foo".to_string()
3799 );
3800 assert_eq!(
3801 relations.entries().nth(1).unwrap().to_string(),
3802 "bar".to_string()
3803 );
3804 relations.remove_entry(1);
3805 assert_eq!(relations.to_string(), "foo, , ");
3806 }
3807
3808 #[test]
3809 fn test_entry_remove_relation() {
3810 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
3811 let removed = entry.remove_relation(0);
3812 assert_eq!(removed.to_string(), "python3-dulwich");
3813 assert_eq!(entry.to_string(), "samba");
3814 }
3815
3816 #[test]
3817 fn test_wrap_and_sort_removes_empty_entries() {
3818 let relations: Relations = "foo, , bar, ".parse().unwrap();
3819 let wrapped = relations.wrap_and_sort();
3820 assert_eq!(wrapped.to_string(), "bar, foo");
3821 }
3822
3823 #[test]
3824 fn test_set_archqual() {
3825 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
3826 let mut rel = entry.relations().next().unwrap();
3827 rel.set_archqual("amd64");
3828 assert_eq!(rel.to_string(), "python3-dulwich:amd64");
3829 assert_eq!(rel.archqual(), Some("amd64".to_string()));
3830 assert_eq!(entry.to_string(), "python3-dulwich:amd64 | samba");
3831 rel.set_archqual("i386");
3832 assert_eq!(rel.to_string(), "python3-dulwich:i386");
3833 assert_eq!(rel.archqual(), Some("i386".to_string()));
3834 assert_eq!(entry.to_string(), "python3-dulwich:i386 | samba");
3835 }
3836
3837 #[test]
3838 fn test_set_architectures() {
3839 let mut relation = Relation::simple("samba");
3840 relation.set_architectures(vec!["amd64", "i386"].into_iter());
3841 assert_eq!(relation.to_string(), "samba [amd64 i386]");
3842 }
3843
3844 #[test]
3845 fn test_relation_builder_no_architectures() {
3846 let relation = Relation::build("debhelper").build();
3848 assert_eq!(relation.to_string(), "debhelper");
3849 }
3850
3851 #[test]
3852 fn test_relation_builder_with_architectures() {
3853 let relation = Relation::build("samba")
3855 .architectures(vec!["amd64".to_string(), "i386".to_string()])
3856 .build();
3857 assert_eq!(relation.to_string(), "samba [amd64 i386]");
3858 }
3859
3860 #[test]
3861 fn test_ensure_minimum_version_add_new() {
3862 let mut relations: Relations = "python3".parse().unwrap();
3863 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3864 assert_eq!(relations.to_string(), "python3, debhelper (>= 12)");
3865 }
3866
3867 #[test]
3868 fn test_ensure_minimum_version_update() {
3869 let mut relations: Relations = "debhelper (>= 9)".parse().unwrap();
3870 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3871 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3872 }
3873
3874 #[test]
3875 fn test_ensure_minimum_version_no_change() {
3876 let mut relations: Relations = "debhelper (>= 13)".parse().unwrap();
3877 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3878 assert_eq!(relations.to_string(), "debhelper (>= 13)");
3879 }
3880
3881 #[test]
3882 fn test_ensure_minimum_version_no_version() {
3883 let mut relations: Relations = "debhelper".parse().unwrap();
3884 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3885 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3886 }
3887
3888 #[test]
3889 fn test_ensure_minimum_version_preserves_newline() {
3890 let input = "\n debhelper (>= 9),\n pkg-config,\n uuid-dev";
3896 let mut relations: Relations = input.parse().unwrap();
3897 relations.ensure_minimum_version("debhelper", &"12~".parse().unwrap());
3898 let result = relations.to_string();
3899
3900 assert!(
3902 result.starts_with('\n'),
3903 "Expected result to start with newline, got: {:?}",
3904 result
3905 );
3906 assert_eq!(result, "\n debhelper (>= 12~),\n pkg-config,\n uuid-dev");
3907 }
3908
3909 #[test]
3910 fn test_ensure_minimum_version_preserves_newline_in_control() {
3911 use crate::lossless::Control;
3913 use std::str::FromStr;
3914
3915 let input = r#"Source: f2fs-tools
3916Section: admin
3917Priority: optional
3918Maintainer: Test <test@example.com>
3919Build-Depends:
3920 debhelper (>= 9),
3921 pkg-config,
3922 uuid-dev
3923
3924Package: f2fs-tools
3925Description: test
3926"#;
3927
3928 let control = Control::from_str(input).unwrap();
3929 let mut source = control.source().unwrap();
3930 let mut build_depends = source.build_depends().unwrap();
3931
3932 let version = Version::from_str("12~").unwrap();
3933 build_depends.ensure_minimum_version("debhelper", &version);
3934
3935 source.set_build_depends(&build_depends);
3936
3937 let output = control.to_string();
3938
3939 assert!(
3941 output.contains("Build-Depends:\n debhelper (>= 12~)"),
3942 "Expected 'Build-Depends:\\n debhelper (>= 12~)' but got:\n{}",
3943 output
3944 );
3945 }
3946
3947 #[test]
3948 fn test_ensure_exact_version_add_new() {
3949 let mut relations: Relations = "python3".parse().unwrap();
3950 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3951 assert_eq!(relations.to_string(), "python3, debhelper (= 12)");
3952 }
3953
3954 #[test]
3955 fn test_ensure_exact_version_update() {
3956 let mut relations: Relations = "debhelper (>= 9)".parse().unwrap();
3957 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3958 assert_eq!(relations.to_string(), "debhelper (= 12)");
3959 }
3960
3961 #[test]
3962 fn test_ensure_exact_version_no_change() {
3963 let mut relations: Relations = "debhelper (= 12)".parse().unwrap();
3964 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3965 assert_eq!(relations.to_string(), "debhelper (= 12)");
3966 }
3967
3968 #[test]
3969 fn test_ensure_some_version_add_new() {
3970 let mut relations: Relations = "python3".parse().unwrap();
3971 relations.ensure_some_version("debhelper");
3972 assert_eq!(relations.to_string(), "python3, debhelper");
3973 }
3974
3975 #[test]
3976 fn test_ensure_some_version_exists_with_version() {
3977 let mut relations: Relations = "debhelper (>= 12)".parse().unwrap();
3978 relations.ensure_some_version("debhelper");
3979 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3980 }
3981
3982 #[test]
3983 fn test_ensure_some_version_exists_no_version() {
3984 let mut relations: Relations = "debhelper".parse().unwrap();
3985 relations.ensure_some_version("debhelper");
3986 assert_eq!(relations.to_string(), "debhelper");
3987 }
3988
3989 #[test]
3990 fn test_ensure_substvar() {
3991 let mut relations: Relations = "python3".parse().unwrap();
3992 relations.ensure_substvar("${misc:Depends}").unwrap();
3993 assert_eq!(relations.to_string(), "python3, ${misc:Depends}");
3994 }
3995
3996 #[test]
3997 fn test_ensure_substvar_already_exists() {
3998 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
3999 relations.ensure_substvar("${misc:Depends}").unwrap();
4000 assert_eq!(relations.to_string(), "python3, ${misc:Depends}");
4001 }
4002
4003 #[test]
4004 fn test_ensure_substvar_empty_relations() {
4005 let mut relations: Relations = Relations::new();
4006 relations.ensure_substvar("${misc:Depends}").unwrap();
4007 assert_eq!(relations.to_string(), "${misc:Depends}");
4008 }
4009
4010 #[test]
4011 fn test_ensure_substvar_preserves_whitespace() {
4012 let (mut relations, _) = Relations::parse_relaxed("python3, rustc", false);
4014 relations.ensure_substvar("${misc:Depends}").unwrap();
4015 assert_eq!(relations.to_string(), "python3, rustc, ${misc:Depends}");
4017 }
4018
4019 #[test]
4020 fn test_ensure_substvar_to_existing_substvar() {
4021 let (mut relations, _) = Relations::parse_relaxed("${shlibs:Depends}", true);
4024 relations.ensure_substvar("${misc:Depends}").unwrap();
4025 assert_eq!(relations.to_string(), "${shlibs:Depends}, ${misc:Depends}");
4027 }
4028
4029 #[test]
4030 fn test_drop_substvar_basic() {
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_drop_substvar_first_position() {
4038 let (mut relations, _) = Relations::parse_relaxed("${misc:Depends}, python3", true);
4039 relations.drop_substvar("${misc:Depends}");
4040 assert_eq!(relations.to_string(), "python3");
4041 }
4042
4043 #[test]
4044 fn test_drop_substvar_middle_position() {
4045 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}, rustc", true);
4046 relations.drop_substvar("${misc:Depends}");
4047 assert_eq!(relations.to_string(), "python3, rustc");
4048 }
4049
4050 #[test]
4051 fn test_drop_substvar_only_substvar() {
4052 let (mut relations, _) = Relations::parse_relaxed("${misc:Depends}", true);
4053 relations.drop_substvar("${misc:Depends}");
4054 assert_eq!(relations.to_string(), "");
4055 }
4056
4057 #[test]
4058 fn test_drop_substvar_not_exists() {
4059 let (mut relations, _) = Relations::parse_relaxed("python3, rustc", false);
4060 relations.drop_substvar("${misc:Depends}");
4061 assert_eq!(relations.to_string(), "python3, rustc");
4062 }
4063
4064 #[test]
4065 fn test_drop_substvar_multiple_substvars() {
4066 let (mut relations, _) =
4067 Relations::parse_relaxed("python3, ${misc:Depends}, ${shlibs:Depends}", true);
4068 relations.drop_substvar("${misc:Depends}");
4069 assert_eq!(relations.to_string(), "python3, ${shlibs:Depends}");
4070 }
4071
4072 #[test]
4073 fn test_drop_substvar_preserves_whitespace() {
4074 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
4075 relations.drop_substvar("${misc:Depends}");
4076 assert_eq!(relations.to_string(), "python3");
4077 }
4078
4079 #[test]
4080 fn test_filter_entries_basic() {
4081 let mut relations: Relations = "python3, debhelper, rustc".parse().unwrap();
4082 relations.filter_entries(|entry| entry.relations().any(|r| r.name().starts_with("python")));
4083 assert_eq!(relations.to_string(), "python3");
4084 }
4085
4086 #[test]
4087 fn test_filter_entries_keep_all() {
4088 let mut relations: Relations = "python3, debhelper".parse().unwrap();
4089 relations.filter_entries(|_| true);
4090 assert_eq!(relations.to_string(), "python3, debhelper");
4091 }
4092
4093 #[test]
4094 fn test_filter_entries_remove_all() {
4095 let mut relations: Relations = "python3, debhelper".parse().unwrap();
4096 relations.filter_entries(|_| false);
4097 assert_eq!(relations.to_string(), "");
4098 }
4099
4100 #[test]
4101 fn test_filter_entries_keep_middle() {
4102 let mut relations: Relations = "aaa, bbb, ccc".parse().unwrap();
4103 relations.filter_entries(|entry| entry.relations().any(|r| r.name() == "bbb"));
4104 assert_eq!(relations.to_string(), "bbb");
4105 }
4106
4107 #[test]
4110 fn test_is_sorted_wrap_and_sort_order() {
4111 let relations: Relations = "debhelper, python3, rustc".parse().unwrap();
4113 assert!(relations.is_sorted(&WrapAndSortOrder));
4114
4115 let relations: Relations = "rustc, debhelper, python3".parse().unwrap();
4117 assert!(!relations.is_sorted(&WrapAndSortOrder));
4118
4119 let (relations, _) =
4121 Relations::parse_relaxed("cdbs, debhelper-compat, python3, ${misc:Depends}", true);
4122 assert!(relations.is_sorted(&WrapAndSortOrder));
4123 }
4124
4125 #[test]
4126 fn test_is_sorted_default_order() {
4127 let relations: Relations = "aaa, bbb, ccc".parse().unwrap();
4129 assert!(relations.is_sorted(&DefaultSortingOrder));
4130
4131 let relations: Relations = "ccc, aaa, bbb".parse().unwrap();
4133 assert!(!relations.is_sorted(&DefaultSortingOrder));
4134
4135 let (relations, _) = Relations::parse_relaxed("aaa, bbb, ${misc:Depends}", true);
4137 assert!(relations.is_sorted(&DefaultSortingOrder));
4138 }
4139
4140 #[test]
4141 fn test_is_sorted_with_substvars() {
4142 let (relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}, rustc", true);
4144 assert!(relations.is_sorted(&DefaultSortingOrder));
4146 }
4147
4148 #[test]
4149 fn test_drop_dependency_exists() {
4150 let mut relations: Relations = "python3, debhelper, rustc".parse().unwrap();
4151 assert!(relations.drop_dependency("debhelper"));
4152 assert_eq!(relations.to_string(), "python3, rustc");
4153 }
4154
4155 #[test]
4156 fn test_drop_dependency_not_exists() {
4157 let mut relations: Relations = "python3, rustc".parse().unwrap();
4158 assert!(!relations.drop_dependency("nonexistent"));
4159 assert_eq!(relations.to_string(), "python3, rustc");
4160 }
4161
4162 #[test]
4163 fn test_drop_dependency_only_item() {
4164 let mut relations: Relations = "python3".parse().unwrap();
4165 assert!(relations.drop_dependency("python3"));
4166 assert_eq!(relations.to_string(), "");
4167 }
4168
4169 #[test]
4170 fn test_add_dependency_to_empty() {
4171 let mut relations: Relations = "".parse().unwrap();
4172 let entry = Entry::from(Relation::simple("python3"));
4173 relations.add_dependency(entry, None);
4174 assert_eq!(relations.to_string(), "python3");
4175 }
4176
4177 #[test]
4178 fn test_add_dependency_sorted_position() {
4179 let mut relations: Relations = "debhelper, rustc".parse().unwrap();
4180 let entry = Entry::from(Relation::simple("python3"));
4181 relations.add_dependency(entry, None);
4182 assert_eq!(relations.to_string(), "debhelper, python3, rustc");
4184 }
4185
4186 #[test]
4187 fn test_add_dependency_explicit_position() {
4188 let mut relations: Relations = "python3, rustc".parse().unwrap();
4189 let entry = Entry::from(Relation::simple("debhelper"));
4190 relations.add_dependency(entry, Some(0));
4191 assert_eq!(relations.to_string(), "debhelper, python3, rustc");
4192 }
4193
4194 #[test]
4195 fn test_add_dependency_build_system_first() {
4196 let mut relations: Relations = "python3, rustc".parse().unwrap();
4197 let entry = Entry::from(Relation::simple("debhelper-compat"));
4198 relations.add_dependency(entry, None);
4199 assert_eq!(relations.to_string(), "debhelper-compat, python3, rustc");
4201 }
4202
4203 #[test]
4204 fn test_add_dependency_at_end() {
4205 let mut relations: Relations = "debhelper, python3".parse().unwrap();
4206 let entry = Entry::from(Relation::simple("zzz-package"));
4207 relations.add_dependency(entry, None);
4208 assert_eq!(relations.to_string(), "debhelper, python3, zzz-package");
4210 }
4211
4212 #[test]
4213 fn test_add_dependency_to_single_entry() {
4214 let mut relations: Relations = "python3-dulwich".parse().unwrap();
4216 let entry: Entry = "debhelper-compat (= 12)".parse().unwrap();
4217 relations.add_dependency(entry, None);
4218 assert_eq!(
4220 relations.to_string(),
4221 "debhelper-compat (= 12), python3-dulwich"
4222 );
4223 }
4224
4225 #[test]
4226 fn test_get_relation_exists() {
4227 let relations: Relations = "python3, debhelper (>= 12), rustc".parse().unwrap();
4228 let result = relations.get_relation("debhelper");
4229 assert!(result.is_ok());
4230 let (idx, entry) = result.unwrap();
4231 assert_eq!(idx, 1);
4232 assert_eq!(entry.to_string(), "debhelper (>= 12)");
4233 }
4234
4235 #[test]
4236 fn test_get_relation_not_exists() {
4237 let relations: Relations = "python3, rustc".parse().unwrap();
4238 let result = relations.get_relation("nonexistent");
4239 assert_eq!(result, Err("Package nonexistent not found".to_string()));
4240 }
4241
4242 #[test]
4243 fn test_get_relation_complex_rule() {
4244 let relations: Relations = "python3 | python3-minimal, rustc".parse().unwrap();
4245 let result = relations.get_relation("python3");
4246 assert_eq!(
4247 result,
4248 Err("Complex rule for python3, aborting".to_string())
4249 );
4250 }
4251
4252 #[test]
4253 fn test_iter_relations_for_simple() {
4254 let relations: Relations = "python3, debhelper, python3-dev".parse().unwrap();
4255 let entries: Vec<_> = relations.iter_relations_for("python3").collect();
4256 assert_eq!(entries.len(), 1);
4257 assert_eq!(entries[0].0, 0);
4258 assert_eq!(entries[0].1.to_string(), "python3");
4259 }
4260
4261 #[test]
4262 fn test_iter_relations_for_alternatives() {
4263 let relations: Relations = "python3 | python3-minimal, python3-dev".parse().unwrap();
4264 let entries: Vec<_> = relations.iter_relations_for("python3").collect();
4265 assert_eq!(entries.len(), 1);
4267 assert_eq!(entries[0].0, 0);
4268 }
4269
4270 #[test]
4271 fn test_iter_relations_for_not_found() {
4272 let relations: Relations = "python3, rustc".parse().unwrap();
4273 let entries: Vec<_> = relations.iter_relations_for("debhelper").collect();
4274 assert_eq!(entries.len(), 0);
4275 }
4276
4277 #[test]
4278 fn test_has_relation_exists() {
4279 let relations: Relations = "python3, debhelper, rustc".parse().unwrap();
4280 assert!(relations.has_relation("debhelper"));
4281 assert!(relations.has_relation("python3"));
4282 assert!(relations.has_relation("rustc"));
4283 }
4284
4285 #[test]
4286 fn test_has_relation_not_exists() {
4287 let relations: Relations = "python3, rustc".parse().unwrap();
4288 assert!(!relations.has_relation("debhelper"));
4289 }
4290
4291 #[test]
4292 fn test_has_relation_in_alternative() {
4293 let relations: Relations = "python3 | python3-minimal".parse().unwrap();
4294 assert!(relations.has_relation("python3"));
4295 assert!(relations.has_relation("python3-minimal"));
4296 }
4297
4298 #[test]
4299 fn test_sorting_order_wrap_and_sort_build_systems() {
4300 let order = WrapAndSortOrder;
4301 assert!(order.lt("debhelper", "python3"));
4303 assert!(order.lt("debhelper-compat", "rustc"));
4304 assert!(order.lt("cdbs", "aaa"));
4305 assert!(order.lt("dh-python", "python3"));
4306 }
4307
4308 #[test]
4309 fn test_sorting_order_wrap_and_sort_regular_packages() {
4310 let order = WrapAndSortOrder;
4311 assert!(order.lt("aaa", "bbb"));
4313 assert!(order.lt("python3", "rustc"));
4314 assert!(!order.lt("rustc", "python3"));
4315 }
4316
4317 #[test]
4318 fn test_sorting_order_wrap_and_sort_substvars() {
4319 let order = WrapAndSortOrder;
4320 assert!(order.lt("python3", "${misc:Depends}"));
4322 assert!(!order.lt("${misc:Depends}", "python3"));
4323 assert!(!order.ignore("${misc:Depends}"));
4325 }
4326
4327 #[test]
4328 fn test_sorting_order_default_special_items() {
4329 let order = DefaultSortingOrder;
4330 assert!(order.lt("python3", "${misc:Depends}"));
4332 assert!(order.lt("aaa", "@cdbs@"));
4333 assert!(order.ignore("${misc:Depends}"));
4335 assert!(order.ignore("@cdbs@"));
4336 assert!(!order.ignore("python3"));
4337 }
4338
4339 #[test]
4340 fn test_is_special_package_name() {
4341 assert!(is_special_package_name("${misc:Depends}"));
4342 assert!(is_special_package_name("${shlibs:Depends}"));
4343 assert!(is_special_package_name("@cdbs@"));
4344 assert!(!is_special_package_name("python3"));
4345 assert!(!is_special_package_name("debhelper"));
4346 }
4347
4348 #[test]
4349 fn test_add_dependency_with_explicit_position() {
4350 let mut relations: Relations = "python3, rustc".parse().unwrap();
4352 let entry = Entry::from(Relation::simple("debhelper"));
4353 relations.add_dependency(entry, Some(1));
4354 assert_eq!(relations.to_string(), "python3, debhelper, rustc");
4356 }
4357
4358 #[test]
4359 fn test_whitespace_detection_single_space() {
4360 let mut relations: Relations = "python3, rustc".parse().unwrap();
4361 let entry = Entry::from(Relation::simple("debhelper"));
4362 relations.add_dependency(entry, Some(1));
4363 assert_eq!(relations.to_string(), "python3, debhelper, rustc");
4364 }
4365
4366 #[test]
4367 fn test_whitespace_detection_multiple_spaces() {
4368 let mut relations: Relations = "python3, rustc, gcc".parse().unwrap();
4369 let entry = Entry::from(Relation::simple("debhelper"));
4370 relations.add_dependency(entry, Some(1));
4371 assert_eq!(relations.to_string(), "python3, debhelper, rustc, gcc");
4373 }
4374
4375 #[test]
4376 fn test_whitespace_detection_mixed_patterns() {
4377 let mut relations: Relations = "a, b, c, d, e".parse().unwrap();
4379 let entry = Entry::from(Relation::simple("x"));
4380 relations.push(entry);
4381 assert_eq!(relations.to_string(), "a, b, c, d, e, x");
4384 }
4385
4386 #[test]
4387 fn test_whitespace_detection_newlines() {
4388 let mut relations: Relations = "python3,\n rustc".parse().unwrap();
4389 let entry = Entry::from(Relation::simple("debhelper"));
4390 relations.add_dependency(entry, Some(1));
4391 assert_eq!(relations.to_string(), "python3,\n debhelper,\n rustc");
4393 }
4394
4395 #[test]
4396 fn test_append_with_newline_no_trailing() {
4397 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4398 let entry = Entry::from(Relation::simple("blah"));
4399 relations.add_dependency(entry, None);
4400 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4401 }
4402
4403 #[test]
4404 fn test_append_with_trailing_newline() {
4405 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4406 let entry = Entry::from(Relation::simple("blah"));
4407 relations.add_dependency(entry, None);
4408 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4409 }
4410
4411 #[test]
4412 fn test_append_with_4_space_indent() {
4413 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4414 let entry = Entry::from(Relation::simple("blah"));
4415 relations.add_dependency(entry, None);
4416 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4417 }
4418
4419 #[test]
4420 fn test_append_with_4_space_and_trailing_newline() {
4421 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4422 let entry = Entry::from(Relation::simple("blah"));
4423 relations.add_dependency(entry, None);
4424 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4425 }
4426
4427 #[test]
4428 fn test_odd_syntax_append_no_trailing() {
4429 let mut relations: Relations = "\n foo\n , bar".parse().unwrap();
4430 let entry = Entry::from(Relation::simple("blah"));
4431 relations.add_dependency(entry, None);
4432 assert_eq!(relations.to_string(), "\n foo\n , bar\n , blah");
4433 }
4434
4435 #[test]
4436 fn test_odd_syntax_append_with_trailing() {
4437 let mut relations: Relations = "\n foo\n , bar\n".parse().unwrap();
4438 let entry = Entry::from(Relation::simple("blah"));
4439 relations.add_dependency(entry, None);
4440 assert_eq!(relations.to_string(), "\n foo\n , bar\n , blah");
4441 }
4442
4443 #[test]
4444 fn test_insert_at_1_no_trailing() {
4445 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4446 let entry = Entry::from(Relation::simple("blah"));
4447 relations.add_dependency(entry, Some(1));
4448 assert_eq!(relations.to_string(), "foo,\n blah,\n bar");
4449 }
4450
4451 #[test]
4452 fn test_insert_at_1_with_trailing() {
4453 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4454 let entry = Entry::from(Relation::simple("blah"));
4455 relations.add_dependency(entry, Some(1));
4456 assert_eq!(relations.to_string(), "foo,\n blah,\n bar");
4457 }
4458
4459 #[test]
4460 fn test_odd_syntax_insert_at_1() {
4461 let mut relations: Relations = "\n foo\n , bar\n".parse().unwrap();
4462 let entry = Entry::from(Relation::simple("blah"));
4463 relations.add_dependency(entry, Some(1));
4464 assert_eq!(relations.to_string(), "\n foo\n , blah\n , bar");
4465 }
4466
4467 #[test]
4468 fn test_relations_preserves_exact_whitespace() {
4469 let input =
4471 "debhelper (>= 10), quilt (>= 0.40),\n libsystemd-dev [linux-any], pkg-config";
4472
4473 let relations: Relations = input.parse().unwrap();
4474
4475 assert_eq!(
4477 relations.to_string(),
4478 input,
4479 "Relations should preserve exact whitespace from input"
4480 );
4481 }
4482
4483 #[test]
4484 fn test_remove_entry_preserves_indentation() {
4485 let input = "debhelper (>= 10), quilt (>= 0.40),\n libsystemd-dev [linux-any], dh-systemd (>= 1.5), pkg-config";
4487
4488 let mut relations: Relations = input.parse().unwrap();
4489
4490 let mut to_remove = Vec::new();
4492 for (idx, entry) in relations.entries().enumerate() {
4493 for relation in entry.relations() {
4494 if relation.name() == "dh-systemd" {
4495 to_remove.push(idx);
4496 break;
4497 }
4498 }
4499 }
4500
4501 for idx in to_remove.into_iter().rev() {
4502 relations.remove_entry(idx);
4503 }
4504
4505 let output = relations.to_string();
4506 println!("After removal: '{}'", output);
4507
4508 assert!(
4510 output.contains("\n libsystemd-dev"),
4511 "Expected 4-space indentation to be preserved, but got:\n'{}'",
4512 output
4513 );
4514 }
4515
4516 #[test]
4517 fn test_relation_is_implied_by_same_package() {
4518 let inner = Relation::new(
4520 "pkg",
4521 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4522 );
4523 let outer = Relation::new(
4524 "pkg",
4525 Some((VersionConstraint::GreaterThanEqual, "1.5".parse().unwrap())),
4526 );
4527 assert!(inner.is_implied_by(&outer));
4528 }
4529
4530 #[test]
4531 fn test_relation_is_implied_by_different_package() {
4532 let inner = Relation::new("pkg1", None);
4534 let outer = Relation::new("pkg2", None);
4535 assert!(!inner.is_implied_by(&outer));
4536 }
4537
4538 #[test]
4539 fn test_relation_is_implied_by_no_version() {
4540 let inner = Relation::new("pkg", None);
4542 let outer = Relation::new(
4543 "pkg",
4544 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4545 );
4546 assert!(inner.is_implied_by(&outer));
4547 }
4548
4549 #[test]
4550 fn test_relation_is_implied_by_identical() {
4551 let inner = Relation::new(
4553 "pkg",
4554 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4555 );
4556 let outer = Relation::new(
4557 "pkg",
4558 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4559 );
4560 assert!(inner.is_implied_by(&outer));
4561 assert!(outer.is_implied_by(&inner));
4562 }
4563
4564 #[test]
4565 fn test_relation_is_implied_by_greater_than_equal() {
4566 let inner = Relation::new(
4568 "pkg",
4569 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4570 );
4571 let outer = Relation::new(
4572 "pkg",
4573 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
4574 );
4575 assert!(inner.is_implied_by(&outer));
4576 assert!(!outer.is_implied_by(&inner));
4577
4578 let outer = Relation::new(
4580 "pkg",
4581 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4582 );
4583 assert!(inner.is_implied_by(&outer));
4584
4585 let outer = Relation::new(
4587 "pkg",
4588 Some((VersionConstraint::GreaterThan, "1.5".parse().unwrap())),
4589 );
4590 assert!(inner.is_implied_by(&outer));
4591
4592 let inner = Relation::new(
4594 "pkg",
4595 Some((VersionConstraint::GreaterThanEqual, "3.0".parse().unwrap())),
4596 );
4597 let outer = Relation::new(
4598 "pkg",
4599 Some((VersionConstraint::GreaterThan, "3.0".parse().unwrap())),
4600 );
4601 assert!(!inner.is_implied_by(&outer));
4602 }
4603
4604 #[test]
4605 fn test_relation_is_implied_by_less_than_equal() {
4606 let inner = Relation::new(
4608 "pkg",
4609 Some((VersionConstraint::LessThanEqual, "2.0".parse().unwrap())),
4610 );
4611 let outer = Relation::new(
4612 "pkg",
4613 Some((VersionConstraint::LessThanEqual, "1.0".parse().unwrap())),
4614 );
4615 assert!(inner.is_implied_by(&outer));
4616 assert!(!outer.is_implied_by(&inner));
4617
4618 let outer = Relation::new(
4620 "pkg",
4621 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4622 );
4623 assert!(inner.is_implied_by(&outer));
4624
4625 let outer = Relation::new(
4627 "pkg",
4628 Some((VersionConstraint::LessThan, "1.5".parse().unwrap())),
4629 );
4630 assert!(inner.is_implied_by(&outer));
4631 }
4632
4633 #[test]
4634 fn test_relation_is_implied_by_equal() {
4635 let inner = Relation::new(
4637 "pkg",
4638 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4639 );
4640 let outer = Relation::new(
4641 "pkg",
4642 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4643 );
4644 assert!(inner.is_implied_by(&outer));
4645
4646 let outer = Relation::new(
4648 "pkg",
4649 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4650 );
4651 assert!(!inner.is_implied_by(&outer));
4652
4653 let outer = Relation::new(
4655 "pkg",
4656 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4657 );
4658 assert!(!inner.is_implied_by(&outer));
4659 }
4660
4661 #[test]
4662 fn test_relation_is_implied_by_greater_than() {
4663 let inner = Relation::new(
4665 "pkg",
4666 Some((VersionConstraint::GreaterThan, "1.0".parse().unwrap())),
4667 );
4668 let outer = Relation::new(
4669 "pkg",
4670 Some((VersionConstraint::GreaterThan, "2.0".parse().unwrap())),
4671 );
4672 assert!(inner.is_implied_by(&outer));
4673
4674 let outer = Relation::new(
4676 "pkg",
4677 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4678 );
4679 assert!(inner.is_implied_by(&outer));
4680
4681 let outer = Relation::new(
4683 "pkg",
4684 Some((VersionConstraint::GreaterThanEqual, "1.5".parse().unwrap())),
4685 );
4686 assert!(inner.is_implied_by(&outer));
4687
4688 let outer = Relation::new(
4690 "pkg",
4691 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4692 );
4693 assert!(!inner.is_implied_by(&outer));
4694 }
4695
4696 #[test]
4697 fn test_relation_is_implied_by_less_than() {
4698 let inner = Relation::new(
4700 "pkg",
4701 Some((VersionConstraint::LessThan, "2.0".parse().unwrap())),
4702 );
4703 let outer = Relation::new(
4704 "pkg",
4705 Some((VersionConstraint::LessThan, "1.0".parse().unwrap())),
4706 );
4707 assert!(inner.is_implied_by(&outer));
4708
4709 let outer = Relation::new(
4711 "pkg",
4712 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4713 );
4714 assert!(inner.is_implied_by(&outer));
4715
4716 let outer = Relation::new(
4718 "pkg",
4719 Some((VersionConstraint::LessThanEqual, "1.5".parse().unwrap())),
4720 );
4721 assert!(inner.is_implied_by(&outer));
4722 }
4723
4724 #[test]
4725 fn test_relation_is_implied_by_incompatible_constraints() {
4726 let inner = Relation::new(
4728 "pkg",
4729 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4730 );
4731 let outer = Relation::new(
4732 "pkg",
4733 Some((VersionConstraint::LessThanEqual, "2.0".parse().unwrap())),
4734 );
4735 assert!(!inner.is_implied_by(&outer));
4736 assert!(!outer.is_implied_by(&inner));
4737 }
4738
4739 #[test]
4740 fn test_entry_is_implied_by_identical() {
4741 let inner: Entry = "pkg (>= 1.0)".parse().unwrap();
4742 let outer: Entry = "pkg (>= 1.0)".parse().unwrap();
4743 assert!(inner.is_implied_by(&outer));
4744 }
4745
4746 #[test]
4747 fn test_entry_is_implied_by_or_group() {
4748 let inner: Entry = "pkg (>= 1.0)".parse().unwrap();
4750 let outer: Entry = "pkg (>= 1.5) | libc6".parse().unwrap();
4751 assert!(inner.is_implied_by(&outer));
4752 }
4753
4754 #[test]
4755 fn test_entry_is_implied_by_simple_or() {
4756 let inner: Entry = "pkg1 | pkg2".parse().unwrap();
4758 let outer: Entry = "pkg1".parse().unwrap();
4759 assert!(inner.is_implied_by(&outer));
4760
4761 let outer: Entry = "pkg2".parse().unwrap();
4763 assert!(inner.is_implied_by(&outer));
4764 }
4765
4766 #[test]
4767 fn test_entry_is_implied_by_not_implied() {
4768 let inner: Entry = "pkg (>= 2.0)".parse().unwrap();
4770 let outer: Entry = "pkg (>= 1.0)".parse().unwrap();
4771 assert!(!inner.is_implied_by(&outer));
4772 }
4773
4774 #[test]
4775 fn test_entry_is_implied_by_different_packages() {
4776 let inner: Entry = "pkg1".parse().unwrap();
4777 let outer: Entry = "pkg2".parse().unwrap();
4778 assert!(!inner.is_implied_by(&outer));
4779 }
4780
4781 #[test]
4782 fn test_entry_is_implied_by_complex_or() {
4783 let inner: Entry = "pkg1 | pkg2".parse().unwrap();
4785 let outer: Entry = "pkg1 | pkg2".parse().unwrap();
4786 assert!(inner.is_implied_by(&outer));
4787
4788 let outer: Entry = "pkg1 | pkg2 | pkg3".parse().unwrap();
4790 assert!(inner.is_implied_by(&outer));
4791 }
4792
4793 #[test]
4794 fn test_parse_version_with_epoch() {
4795 let input = "amule-dbg (<< 1:2.3.2-2~)";
4798 let parsed: Relations = input.parse().unwrap();
4799 assert_eq!(parsed.to_string(), input);
4800 assert_eq!(parsed.entries().count(), 1);
4801 let entry = parsed.entries().next().unwrap();
4802 assert_eq!(entry.to_string(), "amule-dbg (<< 1:2.3.2-2~)");
4803 assert_eq!(entry.relations().count(), 1);
4804 let relation = entry.relations().next().unwrap();
4805 assert_eq!(relation.name(), "amule-dbg");
4806 assert_eq!(relation.to_string(), "amule-dbg (<< 1:2.3.2-2~)");
4807 assert_eq!(
4808 relation.version(),
4809 Some((VersionConstraint::LessThan, "1:2.3.2-2~".parse().unwrap()))
4810 );
4811 }
4812
4813 #[test]
4814 fn test_ensure_relation_add_new() {
4815 let mut relations: Relations = "python3".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), python3");
4822 }
4823
4824 #[test]
4825 fn test_ensure_relation_already_satisfied() {
4826 let mut relations: Relations = "debhelper (>= 13)".parse().unwrap();
4828 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4829 let added = relations.ensure_relation(new_entry);
4830 assert!(!added);
4831 assert_eq!(relations.to_string(), "debhelper (>= 13)");
4832 }
4833
4834 #[test]
4835 fn test_ensure_relation_replace_weaker() {
4836 let mut relations: Relations = "debhelper (>= 11)".parse().unwrap();
4838 let new_entry: Entry = "debhelper (>= 13)".parse().unwrap();
4839 let added = relations.ensure_relation(new_entry);
4840 assert!(added);
4841 assert_eq!(relations.to_string(), "debhelper (>= 13)");
4842 }
4843
4844 #[test]
4845 fn test_ensure_relation_replace_multiple_weaker() {
4846 let mut relations: Relations = "debhelper (>= 11), debhelper (>= 10), python3"
4848 .parse()
4849 .unwrap();
4850 let new_entry: Entry = "debhelper (>= 13)".parse().unwrap();
4851 let added = relations.ensure_relation(new_entry);
4852 assert!(added);
4853 assert_eq!(relations.to_string(), "debhelper (>= 13), python3");
4854 }
4855
4856 #[test]
4857 fn test_ensure_relation_identical_entry() {
4858 let mut relations: Relations = "debhelper (>= 12)".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)");
4864 }
4865
4866 #[test]
4867 fn test_ensure_relation_no_version_constraint() {
4868 let mut relations: Relations = "python3".parse().unwrap();
4870 let new_entry: Entry = "debhelper".parse().unwrap();
4871 let added = relations.ensure_relation(new_entry);
4872 assert!(added);
4873 assert_eq!(relations.to_string(), "debhelper, python3");
4875 }
4876
4877 #[test]
4878 fn test_ensure_relation_strengthen_unversioned() {
4879 let mut relations: Relations = "debhelper".parse().unwrap();
4882 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4883 let added = relations.ensure_relation(new_entry);
4884 assert!(added);
4885 assert_eq!(relations.to_string(), "debhelper (>= 12)");
4886 }
4887
4888 #[test]
4889 fn test_ensure_relation_versioned_implies_unversioned() {
4890 let mut relations: Relations = "debhelper (>= 12)".parse().unwrap();
4893 let new_entry: Entry = "debhelper".parse().unwrap();
4894 let added = relations.ensure_relation(new_entry);
4895 assert!(!added);
4896 assert_eq!(relations.to_string(), "debhelper (>= 12)");
4897 }
4898
4899 #[test]
4900 fn test_ensure_relation_preserves_whitespace() {
4901 let mut relations: Relations = "python3, rustc".parse().unwrap();
4903 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4904 let added = relations.ensure_relation(new_entry);
4905 assert!(added);
4906 assert_eq!(relations.to_string(), "debhelper (>= 12), python3, rustc");
4908 }
4909
4910 #[test]
4911 fn test_ensure_relation_empty_relations() {
4912 let mut relations: Relations = Relations::new();
4914 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4915 let added = relations.ensure_relation(new_entry);
4916 assert!(added);
4917 assert_eq!(relations.to_string(), "debhelper (>= 12)");
4918 }
4919
4920 #[test]
4921 fn test_ensure_relation_alternative_dependencies() {
4922 let mut relations: Relations = "python3 | python3-minimal".parse().unwrap();
4924 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4925 let added = relations.ensure_relation(new_entry);
4926 assert!(added);
4927 assert_eq!(
4929 relations.to_string(),
4930 "debhelper (>= 12), python3 | python3-minimal"
4931 );
4932 }
4933
4934 #[test]
4935 fn test_ensure_relation_replace_in_middle() {
4936 let mut relations: Relations = "python3, debhelper (>= 11), rustc".parse().unwrap();
4938 let new_entry: Entry = "debhelper (>= 13)".parse().unwrap();
4939 let added = relations.ensure_relation(new_entry);
4940 assert!(added);
4941 assert_eq!(relations.to_string(), "python3, debhelper (>= 13), rustc");
4942 }
4943
4944 #[test]
4945 fn test_ensure_relation_with_different_package() {
4946 let mut relations: Relations = "python3, debhelper (>= 12)".parse().unwrap();
4948 let new_entry: Entry = "rustc".parse().unwrap();
4949 let added = relations.ensure_relation(new_entry);
4950 assert!(added);
4951 assert_eq!(relations.to_string(), "python3, debhelper (>= 12), rustc");
4952 }
4953
4954 #[test]
4955 fn test_parse_invalid_token_in_arch_list() {
4956 let input = "foo [>= bar]";
4957 let result: Result<Relations, _> = input.parse();
4958 assert!(
4959 result.is_err(),
4960 "Expected error for invalid token in architecture list"
4961 );
4962 }
4963
4964 #[test]
4965 fn test_parse_invalid_token_in_profile_list() {
4966 let input = "foo <[] baz>";
4967 let result: Result<Relations, _> = input.parse();
4968 assert!(
4969 result.is_err(),
4970 "Expected error for invalid token in profile list"
4971 );
4972 }
4973
4974 #[test]
4975 fn test_parse_relaxed_unterminated_arch_list() {
4976 let (relations, errors) = Relations::parse_relaxed("libc6 [", true);
4977 assert!(!errors.is_empty());
4978 assert_eq!(relations.to_string(), "libc6 [");
4979 }
4980
4981 #[test]
4982 fn test_parse_relaxed_partial_arch_name() {
4983 let (relations, errors) = Relations::parse_relaxed("libc6 [amd", true);
4984 assert!(!errors.is_empty());
4985 assert_eq!(relations.to_string(), "libc6 [amd");
4986 }
4987
4988 #[test]
4989 fn test_parse_relaxed_unterminated_profile_list() {
4990 let (relations, errors) = Relations::parse_relaxed("libc6 <cross", true);
4991 assert!(!errors.is_empty());
4992 assert_eq!(relations.to_string(), "libc6 <cross");
4993 }
4994
4995 #[test]
4996 fn test_parse_relaxed_unterminated_substvar() {
4997 let (relations, errors) = Relations::parse_relaxed("${shlibs:Depends", true);
4998 assert!(!errors.is_empty());
4999 assert_eq!(relations.to_string(), "${shlibs:Depends");
5000 }
5001
5002 #[test]
5003 fn test_parse_relaxed_empty_substvar() {
5004 let (relations, errors) = Relations::parse_relaxed("${", true);
5005 assert!(!errors.is_empty());
5006 assert_eq!(relations.to_string(), "${");
5007 }
5008
5009 #[test]
5010 fn test_parse_with_comments() {
5011 let input = "dh-python,\nlibsvn-dev,\n# python-all-dbg (>= 2.6.6-3),\npython3-all-dev,\n# python3-all-dbg,\npython3-docutils";
5012 let relations: Relations = input.parse().unwrap();
5013 let entries: Vec<_> = relations.entries().collect();
5014 assert_eq!(entries.len(), 4);
5015 assert_eq!(entries[0].to_string(), "dh-python");
5016 assert_eq!(entries[1].to_string(), "libsvn-dev");
5017 assert_eq!(entries[2].to_string(), "python3-all-dev");
5018 assert_eq!(entries[3].to_string(), "python3-docutils");
5019 assert_eq!(relations.to_string(), input);
5021 }
5022
5023 #[test]
5024 fn test_remove_entry_with_adjacent_comment() {
5025 let input = "dh-python,\n# commented-out,\npython3-all-dev";
5026 let mut relations: Relations = input.parse().unwrap();
5027 assert_eq!(relations.entries().count(), 2);
5028 relations.remove_entry(0);
5029 assert_eq!(relations.entries().count(), 1);
5030 assert_eq!(
5031 relations.entries().next().unwrap().to_string(),
5032 "python3-all-dev"
5033 );
5034 }
5035
5036 #[test]
5037 fn test_insert_entry_with_comments_present() {
5038 let input = "dh-python,\n# commented-out,\npython3-all-dev";
5039 let mut relations: Relations = input.parse().unwrap();
5040 let new_entry: Entry = "libfoo-dev".parse().unwrap();
5041 relations.push(new_entry);
5042 let entries: Vec<_> = relations.entries().collect();
5044 assert_eq!(entries.len(), 3);
5045 assert_eq!(entries[2].to_string(), "libfoo-dev");
5046 }
5047
5048 #[test]
5049 fn test_drop_dependency_with_comments() {
5050 let input = "dh-python,\n# commented-out,\npython3-all-dev,\nlibfoo-dev";
5051 let mut relations: Relations = input.parse().unwrap();
5052 assert!(relations.drop_dependency("python3-all-dev"));
5053 let entries: Vec<_> = relations.entries().collect();
5054 assert_eq!(entries.len(), 2);
5055 assert_eq!(entries[0].to_string(), "dh-python");
5056 assert_eq!(entries[1].to_string(), "libfoo-dev");
5057 }
5058
5059 #[test]
5060 fn test_relation_remove_first_with_malformed_tree() {
5061 let (relations, errors) = Relations::parse_relaxed("foo @ bar | baz", false);
5064 assert!(!errors.is_empty());
5065 let entry = relations.get_entry(0).unwrap();
5066 let mut relation = entry.get_relation(0).unwrap();
5067 relation.remove();
5069 }
5070}