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.substvar_nodes().map(|s| s.to_string())
1030 }
1031
1032 pub fn substvar_nodes(&self) -> impl Iterator<Item = Substvar> + '_ {
1034 self.0.children().filter_map(Substvar::cast)
1035 }
1036
1037 pub fn parse_relaxed(s: &str, allow_substvar: bool) -> (Relations, Vec<String>) {
1039 let parse = parse(s, allow_substvar);
1040 (parse.root_mut(), parse.errors)
1041 }
1042
1043 pub fn satisfied_by(&self, package_version: impl crate::VersionLookup + Copy) -> bool {
1045 self.entries().all(|e| e.satisfied_by(package_version))
1046 }
1047
1048 pub fn is_empty(&self) -> bool {
1050 self.entries().count() == 0
1051 }
1052
1053 pub fn len(&self) -> usize {
1055 self.entries().count()
1056 }
1057
1058 pub fn ensure_minimum_version(&mut self, package: &str, minimum_version: &Version) {
1081 let mut found = false;
1082 let mut obsolete_indices = vec![];
1083 let mut update_idx = None;
1084
1085 let entries: Vec<_> = self.entries().collect();
1086 for (idx, entry) in entries.iter().enumerate() {
1087 let relations: Vec<_> = entry.relations().collect();
1088
1089 let names: Vec<_> = relations.iter().filter_map(|r| r.try_name()).collect();
1091 if names.len() > 1 && names.contains(&package.to_string()) {
1092 let is_obsolete = relations.iter().any(|r| {
1094 if r.try_name().as_deref() != Some(package) {
1095 return false;
1096 }
1097 if let Some((vc, ver)) = r.version() {
1098 matches!(vc, VersionConstraint::GreaterThan if &ver < minimum_version)
1099 || matches!(vc, VersionConstraint::GreaterThanEqual if &ver <= minimum_version)
1100 } else {
1101 false
1102 }
1103 });
1104 if is_obsolete {
1105 obsolete_indices.push(idx);
1106 }
1107 continue;
1108 }
1109
1110 if names.len() == 1 && names[0] == package {
1112 found = true;
1113 let relation = relations.into_iter().next().unwrap();
1114
1115 let should_update = if let Some((vc, ver)) = relation.version() {
1117 match vc {
1118 VersionConstraint::GreaterThanEqual | VersionConstraint::GreaterThan => {
1119 &ver < minimum_version
1120 }
1121 _ => false,
1122 }
1123 } else {
1124 true
1125 };
1126
1127 if should_update {
1128 update_idx = Some(idx);
1129 }
1130 break;
1131 }
1132 }
1133
1134 if let Some(idx) = update_idx {
1136 let relation = Relation::new(
1137 package,
1138 Some((VersionConstraint::GreaterThanEqual, minimum_version.clone())),
1139 );
1140 let mut entry = self.get_entry(idx).unwrap();
1142 entry.replace(0, relation);
1143 self.replace(idx, entry);
1144 }
1145
1146 for idx in obsolete_indices.into_iter().rev() {
1148 self.remove_entry(idx);
1149 }
1150
1151 if !found {
1153 let relation = Relation::new(
1154 package,
1155 Some((VersionConstraint::GreaterThanEqual, minimum_version.clone())),
1156 );
1157 self.push(Entry::from(relation));
1158 }
1159 }
1160
1161 pub fn ensure_exact_version(&mut self, package: &str, version: &Version) {
1176 let mut found = false;
1177 let mut update_idx = None;
1178
1179 let entries: Vec<_> = self.entries().collect();
1180 for (idx, entry) in entries.iter().enumerate() {
1181 let relations: Vec<_> = entry.relations().collect();
1182 let names: Vec<_> = relations.iter().filter_map(|r| r.try_name()).collect();
1183
1184 if names.len() > 1 && names[0] == package {
1185 panic!("Complex rule for {}, aborting", package);
1186 }
1187
1188 if names.len() == 1 && names[0] == package {
1189 found = true;
1190 let relation = relations.into_iter().next().unwrap();
1191
1192 let should_update = if let Some((vc, ver)) = relation.version() {
1193 vc != VersionConstraint::Equal || &ver != version
1194 } else {
1195 true
1196 };
1197
1198 if should_update {
1199 update_idx = Some(idx);
1200 }
1201 break;
1202 }
1203 }
1204
1205 if let Some(idx) = update_idx {
1207 let relation =
1208 Relation::new(package, Some((VersionConstraint::Equal, version.clone())));
1209 let mut entry = self.get_entry(idx).unwrap();
1211 entry.replace(0, relation);
1212 self.replace(idx, entry);
1213 }
1214
1215 if !found {
1216 let relation =
1217 Relation::new(package, Some((VersionConstraint::Equal, version.clone())));
1218 self.push(Entry::from(relation));
1219 }
1220 }
1221
1222 pub fn ensure_some_version(&mut self, package: &str) {
1240 for entry in self.entries() {
1241 let relations: Vec<_> = entry.relations().collect();
1242 let names: Vec<_> = relations.iter().filter_map(|r| r.try_name()).collect();
1243
1244 if names.len() > 1 && names[0] == package {
1245 panic!("Complex rule for {}, aborting", package);
1246 }
1247
1248 if names.len() == 1 && names[0] == package {
1249 return;
1251 }
1252 }
1253
1254 let relation = Relation::simple(package);
1256 self.push(Entry::from(relation));
1257 }
1258
1259 pub fn ensure_relation(&mut self, new_entry: Entry) -> bool {
1284 let mut to_replace: Vec<usize> = Vec::new();
1285 let mut to_remove: Vec<usize> = Vec::new();
1286 let mut already_satisfied = false;
1287
1288 for (idx, existing_entry) in self.entries().enumerate() {
1290 if new_entry.is_implied_by(&existing_entry) {
1291 already_satisfied = true;
1293 break;
1294 }
1295 if existing_entry.is_implied_by(&new_entry) {
1296 if to_replace.is_empty() {
1299 to_replace.push(idx);
1300 } else {
1301 to_remove.push(idx);
1302 }
1303 }
1304 }
1305
1306 if already_satisfied {
1307 return false;
1308 }
1309
1310 for idx in to_remove.into_iter().rev() {
1312 self.remove_entry(idx);
1313 }
1314
1315 if let Some(&idx) = to_replace.first() {
1317 self.replace(idx, new_entry);
1318 } else {
1319 self.add_dependency(new_entry, None);
1320 }
1321
1322 true
1323 }
1324
1325 pub fn ensure_substvar(&mut self, substvar: &str) -> Result<(), String> {
1345 for existing in self.substvars() {
1347 if existing.trim() == substvar.trim() {
1348 return Ok(());
1349 }
1350 }
1351
1352 let (parsed, errors) = Relations::parse_relaxed(substvar, true);
1354 if !errors.is_empty() {
1355 return Err(errors.join("\n"));
1356 }
1357
1358 let whitespace = self.detect_whitespace_pattern(" ");
1360
1361 for substvar_node in parsed.0.children().filter(|n| n.kind() == SUBSTVAR) {
1363 let has_content = self.entries().next().is_some() || self.substvars().next().is_some();
1364
1365 let mut builder = GreenNodeBuilder::new();
1366 builder.start_node(ROOT.into());
1367
1368 for child in self.0.children_with_tokens() {
1370 match child {
1371 NodeOrToken::Node(n) => inject(&mut builder, n),
1372 NodeOrToken::Token(t) => builder.token(t.kind().into(), t.text()),
1373 }
1374 }
1375
1376 if has_content {
1378 builder.token(COMMA.into(), ",");
1379 builder.token(WHITESPACE.into(), whitespace.as_str());
1380 }
1381
1382 inject(&mut builder, substvar_node);
1384
1385 builder.finish_node();
1386 self.0 = SyntaxNode::new_root_mut(builder.finish());
1387 }
1388
1389 Ok(())
1390 }
1391
1392 pub fn drop_substvar(&mut self, substvar: &str) {
1409 let substvars_to_remove: Vec<_> = self
1411 .0
1412 .children()
1413 .filter_map(Substvar::cast)
1414 .filter(|s| s.to_string().trim() == substvar.trim())
1415 .collect();
1416
1417 for substvar_node in substvars_to_remove {
1418 let is_first = !substvar_node
1420 .0
1421 .siblings(Direction::Prev)
1422 .skip(1)
1423 .any(|n| n.kind() == ENTRY || n.kind() == SUBSTVAR);
1424
1425 let mut removed_comma = false;
1426
1427 while let Some(n) = substvar_node.0.next_sibling_or_token() {
1429 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
1430 n.detach();
1431 } else if n.kind() == COMMA {
1432 n.detach();
1433 removed_comma = true;
1434 break;
1435 } else {
1436 break;
1437 }
1438 }
1439
1440 if !is_first {
1442 while let Some(n) = substvar_node.0.prev_sibling_or_token() {
1443 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
1444 n.detach();
1445 } else if !removed_comma && n.kind() == COMMA {
1446 n.detach();
1447 break;
1448 } else {
1449 break;
1450 }
1451 }
1452 } else {
1453 while let Some(n) = substvar_node.0.next_sibling_or_token() {
1455 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
1456 n.detach();
1457 } else {
1458 break;
1459 }
1460 }
1461 }
1462
1463 substvar_node.0.detach();
1465 }
1466 }
1467
1468 pub fn filter_entries<F>(&mut self, keep: F)
1484 where
1485 F: Fn(&Entry) -> bool,
1486 {
1487 let indices_to_remove: Vec<_> = self
1488 .entries()
1489 .enumerate()
1490 .filter_map(|(idx, entry)| if keep(&entry) { None } else { Some(idx) })
1491 .collect();
1492
1493 for idx in indices_to_remove.into_iter().rev() {
1495 self.remove_entry(idx);
1496 }
1497 }
1498
1499 pub fn is_sorted(&self, sorting_order: &impl SortingOrder) -> bool {
1515 let mut last_name: Option<String> = None;
1516 for entry in self.entries() {
1517 let mut relations = entry.relations();
1519 let Some(relation) = relations.next() else {
1520 continue;
1521 };
1522
1523 let Some(name) = relation.try_name() else {
1524 continue;
1525 };
1526
1527 if sorting_order.ignore(&name) {
1529 continue;
1530 }
1531
1532 if let Some(ref last) = last_name {
1534 if sorting_order.lt(&name, last) {
1535 return false;
1536 }
1537 }
1538
1539 last_name = Some(name);
1540 }
1541 true
1542 }
1543
1544 fn find_insert_position(&self, entry: &Entry) -> usize {
1556 let Some(relation) = entry.relations().next() else {
1558 return self.len();
1560 };
1561 let Some(package_name) = relation.try_name() else {
1562 return self.len();
1563 };
1564
1565 let count = self.entries().filter(|e| !e.is_empty()).count();
1567
1568 let sorting_order: Box<dyn SortingOrder> = if count < 2 {
1570 Box::new(WrapAndSortOrder)
1571 } else {
1572 if self.is_sorted(&WrapAndSortOrder) {
1575 Box::new(WrapAndSortOrder)
1576 } else if self.is_sorted(&DefaultSortingOrder) {
1577 Box::new(DefaultSortingOrder)
1578 } else {
1579 return self.len();
1581 }
1582 };
1583
1584 if sorting_order.ignore(&package_name) {
1586 return self.len();
1587 }
1588
1589 let mut position = 0;
1591 for (idx, existing_entry) in self.entries().enumerate() {
1592 let mut existing_relations = existing_entry.relations();
1593 let Some(existing_relation) = existing_relations.next() else {
1594 position += 1;
1596 continue;
1597 };
1598
1599 let Some(existing_name) = existing_relation.try_name() else {
1600 position += 1;
1601 continue;
1602 };
1603
1604 if sorting_order.ignore(&existing_name) {
1606 position += 1;
1607 continue;
1608 }
1609
1610 if sorting_order.lt(&package_name, &existing_name) {
1612 return idx;
1613 }
1614 position += 1;
1615 }
1616
1617 position
1618 }
1619
1620 pub fn drop_dependency(&mut self, package: &str) -> bool {
1638 let indices_to_remove: Vec<_> = self
1639 .entries()
1640 .enumerate()
1641 .filter_map(|(idx, entry)| {
1642 let relations: Vec<_> = entry.relations().collect();
1643 let names: Vec<_> = relations.iter().filter_map(|r| r.try_name()).collect();
1644 if names == vec![package] {
1645 Some(idx)
1646 } else {
1647 None
1648 }
1649 })
1650 .collect();
1651
1652 let found = !indices_to_remove.is_empty();
1653
1654 for idx in indices_to_remove.into_iter().rev() {
1656 self.remove_entry(idx);
1657 }
1658
1659 found
1660 }
1661
1662 pub fn add_dependency(&mut self, entry: Entry, position: Option<usize>) {
1683 let pos = position.unwrap_or_else(|| self.find_insert_position(&entry));
1684 self.insert(pos, entry);
1685 }
1686
1687 pub fn get_relation(&self, package: &str) -> Result<(usize, Entry), String> {
1711 for (idx, entry) in self.entries().enumerate() {
1712 let relations: Vec<_> = entry.relations().collect();
1713 let names: Vec<_> = relations.iter().filter_map(|r| r.try_name()).collect();
1714
1715 if names.len() > 1 && names.contains(&package.to_string()) {
1716 return Err(format!("Complex rule for {}, aborting", package));
1717 }
1718
1719 if names.len() == 1 && names[0] == package {
1720 return Ok((idx, entry));
1721 }
1722 }
1723 Err(format!("Package {} not found", package))
1724 }
1725
1726 pub fn iter_relations_for(&self, package: &str) -> impl Iterator<Item = (usize, Entry)> + '_ {
1743 let package = package.to_string();
1744 self.entries().enumerate().filter(move |(_, entry)| {
1745 let names: Vec<_> = entry.relations().filter_map(|r| r.try_name()).collect();
1746 names.contains(&package)
1747 })
1748 }
1749
1750 pub fn has_relation(&self, package: &str) -> bool {
1767 self.entries().any(|entry| {
1768 entry
1769 .relations()
1770 .any(|r| r.try_name().as_deref() == Some(package))
1771 })
1772 }
1773}
1774
1775impl From<Vec<Entry>> for Relations {
1776 fn from(entries: Vec<Entry>) -> Self {
1777 let mut builder = GreenNodeBuilder::new();
1778 builder.start_node(ROOT.into());
1779 for (i, entry) in entries.into_iter().enumerate() {
1780 if i > 0 {
1781 builder.token(COMMA.into(), ",");
1782 builder.token(WHITESPACE.into(), " ");
1783 }
1784 inject(&mut builder, entry.0);
1785 }
1786 builder.finish_node();
1787 Relations(SyntaxNode::new_root_mut(builder.finish()))
1788 }
1789}
1790
1791impl From<Entry> for Relations {
1792 fn from(entry: Entry) -> Self {
1793 Self::from(vec![entry])
1794 }
1795}
1796
1797impl Default for Entry {
1798 fn default() -> Self {
1799 Self::new()
1800 }
1801}
1802
1803impl PartialOrd for Entry {
1804 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1805 Some(self.cmp(other))
1806 }
1807}
1808
1809impl Eq for Entry {}
1810
1811impl Ord for Entry {
1812 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1813 let mut rels_a = self.relations();
1814 let mut rels_b = other.relations();
1815 while let (Some(a), Some(b)) = (rels_a.next(), rels_b.next()) {
1816 match a.cmp(&b) {
1817 std::cmp::Ordering::Equal => continue,
1818 x => return x,
1819 }
1820 }
1821
1822 if rels_a.next().is_some() {
1823 return std::cmp::Ordering::Greater;
1824 }
1825
1826 if rels_b.next().is_some() {
1827 return std::cmp::Ordering::Less;
1828 }
1829
1830 std::cmp::Ordering::Equal
1831 }
1832}
1833
1834impl Entry {
1835 pub fn new() -> Self {
1837 let mut builder = GreenNodeBuilder::new();
1838 builder.start_node(SyntaxKind::ENTRY.into());
1839 builder.finish_node();
1840 Entry(SyntaxNode::new_root_mut(builder.finish()))
1841 }
1842
1843 pub fn replace(&mut self, idx: usize, relation: Relation) {
1845 let current_relation = self.get_relation(idx).unwrap();
1846
1847 let old_root = current_relation.0;
1848 let new_root = relation.0;
1849 let mut prev = new_root.first_child_or_token();
1851 let mut new_head_len = 0;
1852 while let Some(p) = prev {
1854 if p.kind() == WHITESPACE || p.kind() == NEWLINE {
1855 new_head_len += 1;
1856 prev = p.next_sibling_or_token();
1857 } else {
1858 break;
1859 }
1860 }
1861 let mut new_tail_len = 0;
1862 let mut next = new_root.last_child_or_token();
1863 while let Some(n) = next {
1864 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1865 new_tail_len += 1;
1866 next = n.prev_sibling_or_token();
1867 } else {
1868 break;
1869 }
1870 }
1871 let mut prev = old_root.first_child_or_token();
1873 let mut old_head = vec![];
1874 while let Some(p) = prev {
1875 if p.kind() == WHITESPACE || p.kind() == NEWLINE {
1876 old_head.push(p.clone());
1877 prev = p.next_sibling_or_token();
1878 } else {
1879 break;
1880 }
1881 }
1882 let mut old_tail = vec![];
1883 let mut next = old_root.last_child_or_token();
1884 while let Some(n) = next {
1885 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1886 old_tail.push(n.clone());
1887 next = n.prev_sibling_or_token();
1888 } else {
1889 break;
1890 }
1891 }
1892 new_root.splice_children(0..new_head_len, old_head);
1893 let tail_pos = new_root.children_with_tokens().count() - new_tail_len;
1894 new_root.splice_children(
1895 tail_pos - new_tail_len..tail_pos,
1896 old_tail.into_iter().rev(),
1897 );
1898 let index = old_root.index();
1899 self.0
1900 .splice_children(index..index + 1, vec![new_root.into()]);
1901 }
1902
1903 #[must_use]
1905 pub fn wrap_and_sort(&self) -> Self {
1906 let mut relations = self
1907 .relations()
1908 .map(|r| r.wrap_and_sort())
1909 .collect::<Vec<_>>();
1910 relations.sort();
1912 Self::from(relations)
1913 }
1914
1915 pub fn relations(&self) -> impl Iterator<Item = Relation> + '_ {
1917 self.0.children().filter_map(Relation::cast)
1918 }
1919
1920 pub fn iter(&self) -> impl Iterator<Item = Relation> + '_ {
1922 self.relations()
1923 }
1924
1925 pub fn get_relation(&self, idx: usize) -> Option<Relation> {
1927 self.relations().nth(idx)
1928 }
1929
1930 pub fn remove_relation(&self, idx: usize) -> Relation {
1943 let mut relation = self.get_relation(idx).unwrap();
1944 relation.remove();
1945 relation
1946 }
1947
1948 pub fn satisfied_by(&self, package_version: impl crate::VersionLookup + Copy) -> bool {
1964 self.relations().any(|r| {
1965 let Some(name) = r.try_name() else {
1966 return false;
1967 };
1968 let actual = package_version.lookup_version(name.as_str());
1969 if let Some((vc, version)) = r.version() {
1970 if let Some(actual) = actual {
1971 match vc {
1972 VersionConstraint::GreaterThanEqual => *actual >= version,
1973 VersionConstraint::LessThanEqual => *actual <= version,
1974 VersionConstraint::Equal => *actual == version,
1975 VersionConstraint::GreaterThan => *actual > version,
1976 VersionConstraint::LessThan => *actual < version,
1977 }
1978 } else {
1979 false
1980 }
1981 } else {
1982 actual.is_some()
1983 }
1984 })
1985 }
1986
1987 pub fn remove(&mut self) {
1998 let mut removed_comma = false;
1999 let is_first = !self
2000 .0
2001 .siblings(Direction::Prev)
2002 .skip(1)
2003 .any(|n| n.kind() == ENTRY);
2004 while let Some(n) = self.0.next_sibling_or_token() {
2005 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
2006 n.detach();
2007 } else if n.kind() == COMMA {
2008 n.detach();
2009 removed_comma = true;
2010 break;
2011 } else {
2012 panic!("Unexpected node: {:?}", n);
2013 }
2014 }
2015 if !is_first {
2016 while let Some(n) = self.0.prev_sibling_or_token() {
2017 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
2018 n.detach();
2019 } else if !removed_comma && n.kind() == COMMA {
2020 n.detach();
2021 break;
2022 } else {
2023 break;
2024 }
2025 }
2026 } else {
2027 while let Some(n) = self.0.next_sibling_or_token() {
2028 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
2029 n.detach();
2030 } else {
2031 break;
2032 }
2033 }
2034 }
2035 self.0.detach();
2036 }
2037
2038 pub fn is_empty(&self) -> bool {
2040 self.relations().count() == 0
2041 }
2042
2043 pub fn len(&self) -> usize {
2045 self.relations().count()
2046 }
2047
2048 pub fn push(&mut self, relation: Relation) {
2061 let is_empty = !self
2062 .0
2063 .children_with_tokens()
2064 .any(|n| n.kind() == PIPE || n.kind() == RELATION);
2065
2066 let (position, new_children) = if let Some(current_relation) = self.relations().last() {
2067 let to_insert: Vec<NodeOrToken<GreenNode, GreenToken>> = if is_empty {
2068 vec![relation.0.green().into()]
2069 } else {
2070 vec![
2071 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
2072 NodeOrToken::Token(GreenToken::new(PIPE.into(), "|")),
2073 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
2074 relation.0.green().into(),
2075 ]
2076 };
2077
2078 (current_relation.0.index() + 1, to_insert)
2079 } else {
2080 let child_count = self.0.children_with_tokens().count();
2081 (
2082 child_count,
2083 if is_empty {
2084 vec![relation.0.green().into()]
2085 } else {
2086 vec![
2087 NodeOrToken::Token(GreenToken::new(PIPE.into(), "|")),
2088 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
2089 relation.0.green().into(),
2090 ]
2091 },
2092 )
2093 };
2094
2095 let new_root = SyntaxNode::new_root_mut(
2096 self.0.replace_with(
2097 self.0
2098 .green()
2099 .splice_children(position..position, new_children),
2100 ),
2101 );
2102
2103 if let Some(parent) = self.0.parent() {
2104 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2105 self.0 = parent
2106 .children_with_tokens()
2107 .nth(self.0.index())
2108 .unwrap()
2109 .clone()
2110 .into_node()
2111 .unwrap();
2112 } else {
2113 self.0 = new_root;
2114 }
2115 }
2116
2117 pub fn is_implied_by(&self, outer: &Entry) -> bool {
2142 if self == outer {
2144 return true;
2145 }
2146
2147 for inner_rel in self.relations() {
2149 for outer_rel in outer.relations() {
2150 if inner_rel.is_implied_by(&outer_rel) {
2151 return true;
2152 }
2153 }
2154 }
2155
2156 false
2157 }
2158}
2159
2160fn inject(builder: &mut GreenNodeBuilder, node: SyntaxNode) {
2161 builder.start_node(node.kind().into());
2162 for child in node.children_with_tokens() {
2163 match child {
2164 rowan::NodeOrToken::Node(child) => {
2165 inject(builder, child);
2166 }
2167 rowan::NodeOrToken::Token(token) => {
2168 builder.token(token.kind().into(), token.text());
2169 }
2170 }
2171 }
2172 builder.finish_node();
2173}
2174
2175impl From<Vec<Relation>> for Entry {
2176 fn from(relations: Vec<Relation>) -> Self {
2177 let mut builder = GreenNodeBuilder::new();
2178 builder.start_node(SyntaxKind::ENTRY.into());
2179 for (i, relation) in relations.into_iter().enumerate() {
2180 if i > 0 {
2181 builder.token(WHITESPACE.into(), " ");
2182 builder.token(COMMA.into(), "|");
2183 builder.token(WHITESPACE.into(), " ");
2184 }
2185 inject(&mut builder, relation.0);
2186 }
2187 builder.finish_node();
2188 Entry(SyntaxNode::new_root_mut(builder.finish()))
2189 }
2190}
2191
2192impl From<Relation> for Entry {
2193 fn from(relation: Relation) -> Self {
2194 Self::from(vec![relation])
2195 }
2196}
2197
2198fn tokenize_version(builder: &mut GreenNodeBuilder, version: &Version) {
2201 let version_str = version.to_string();
2202
2203 if let Some(colon_pos) = version_str.find(':') {
2205 builder.token(IDENT.into(), &version_str[..colon_pos]);
2207 builder.token(COLON.into(), ":");
2208 builder.token(IDENT.into(), &version_str[colon_pos + 1..]);
2210 } else {
2211 builder.token(IDENT.into(), version_str.as_str());
2213 }
2214}
2215
2216impl Relation {
2217 pub fn new(name: &str, version_constraint: Option<(VersionConstraint, Version)>) -> Self {
2231 let mut builder = GreenNodeBuilder::new();
2232 builder.start_node(SyntaxKind::RELATION.into());
2233 builder.token(IDENT.into(), name);
2234 if let Some((vc, version)) = version_constraint {
2235 builder.token(WHITESPACE.into(), " ");
2236 builder.start_node(SyntaxKind::VERSION.into());
2237 builder.token(L_PARENS.into(), "(");
2238 builder.start_node(SyntaxKind::CONSTRAINT.into());
2239 for c in vc.to_string().chars() {
2240 builder.token(
2241 match c {
2242 '>' => R_ANGLE.into(),
2243 '<' => L_ANGLE.into(),
2244 '=' => EQUAL.into(),
2245 _ => unreachable!(),
2246 },
2247 c.to_string().as_str(),
2248 );
2249 }
2250 builder.finish_node();
2251
2252 builder.token(WHITESPACE.into(), " ");
2253
2254 tokenize_version(&mut builder, &version);
2255
2256 builder.token(R_PARENS.into(), ")");
2257
2258 builder.finish_node();
2259 }
2260
2261 builder.finish_node();
2262 Relation(SyntaxNode::new_root_mut(builder.finish()))
2263 }
2264
2265 #[must_use]
2274 pub fn wrap_and_sort(&self) -> Self {
2275 let mut builder = GreenNodeBuilder::new();
2276 builder.start_node(SyntaxKind::RELATION.into());
2277 if let Some(name) = self.try_name() {
2278 builder.token(IDENT.into(), name.as_str());
2279 }
2280 if let Some(archqual) = self.archqual() {
2281 builder.token(COLON.into(), ":");
2282 builder.token(IDENT.into(), archqual.as_str());
2283 }
2284 if let Some((vc, version)) = self.version() {
2285 builder.token(WHITESPACE.into(), " ");
2286 builder.start_node(SyntaxKind::VERSION.into());
2287 builder.token(L_PARENS.into(), "(");
2288 builder.start_node(SyntaxKind::CONSTRAINT.into());
2289 builder.token(
2290 match vc {
2291 VersionConstraint::GreaterThanEqual => R_ANGLE.into(),
2292 VersionConstraint::LessThanEqual => L_ANGLE.into(),
2293 VersionConstraint::Equal => EQUAL.into(),
2294 VersionConstraint::GreaterThan => R_ANGLE.into(),
2295 VersionConstraint::LessThan => L_ANGLE.into(),
2296 },
2297 vc.to_string().as_str(),
2298 );
2299 builder.finish_node();
2300 builder.token(WHITESPACE.into(), " ");
2301 tokenize_version(&mut builder, &version);
2302 builder.token(R_PARENS.into(), ")");
2303 builder.finish_node();
2304 }
2305 if let Some(architectures) = self.architectures() {
2306 builder.token(WHITESPACE.into(), " ");
2307 builder.start_node(ARCHITECTURES.into());
2308 builder.token(L_BRACKET.into(), "[");
2309 for (i, arch) in architectures.enumerate() {
2310 if i > 0 {
2311 builder.token(WHITESPACE.into(), " ");
2312 }
2313 builder.token(IDENT.into(), arch.as_str());
2314 }
2315 builder.token(R_BRACKET.into(), "]");
2316 builder.finish_node();
2317 }
2318 for profiles in self.profiles() {
2319 builder.token(WHITESPACE.into(), " ");
2320 builder.start_node(PROFILES.into());
2321 builder.token(L_ANGLE.into(), "<");
2322 for (i, profile) in profiles.into_iter().enumerate() {
2323 if i > 0 {
2324 builder.token(WHITESPACE.into(), " ");
2325 }
2326 match profile {
2327 BuildProfile::Disabled(name) => {
2328 builder.token(NOT.into(), "!");
2329 builder.token(IDENT.into(), name.as_str());
2330 }
2331 BuildProfile::Enabled(name) => {
2332 builder.token(IDENT.into(), name.as_str());
2333 }
2334 }
2335 }
2336 builder.token(R_ANGLE.into(), ">");
2337 builder.finish_node();
2338 }
2339 builder.finish_node();
2340 Relation(SyntaxNode::new_root_mut(builder.finish()))
2341 }
2342
2343 pub fn simple(name: &str) -> Self {
2352 Self::new(name, None)
2353 }
2354
2355 pub fn drop_constraint(&mut self) -> bool {
2366 let version_token = self.0.children().find(|n| n.kind() == VERSION);
2367 if let Some(version_token) = version_token {
2368 while let Some(prev) = version_token.prev_sibling_or_token() {
2370 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
2371 prev.detach();
2372 } else {
2373 break;
2374 }
2375 }
2376 version_token.detach();
2377 return true;
2378 }
2379
2380 false
2381 }
2382
2383 pub fn try_name(&self) -> Option<String> {
2395 self.0
2396 .children_with_tokens()
2397 .find_map(|it| match it {
2398 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2399 _ => None,
2400 })
2401 .map(|token| token.text().to_string())
2402 }
2403
2404 #[deprecated(
2417 since = "0.3.6",
2418 note = "Use try_name() instead, which returns Option<String>"
2419 )]
2420 pub fn name(&self) -> String {
2421 self.try_name().expect("Relation has no package name")
2422 }
2423
2424 pub fn archqual(&self) -> Option<String> {
2433 let archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
2434 let node = if let Some(archqual) = archqual {
2435 archqual.children_with_tokens().find_map(|it| match it {
2436 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2437 _ => None,
2438 })
2439 } else {
2440 None
2441 };
2442 node.map(|n| n.text().to_string())
2443 }
2444
2445 pub fn set_archqual(&mut self, archqual: &str) {
2455 let mut builder = GreenNodeBuilder::new();
2456 builder.start_node(ARCHQUAL.into());
2457 builder.token(COLON.into(), ":");
2458 builder.token(IDENT.into(), archqual);
2459 builder.finish_node();
2460
2461 let node_archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
2462 if let Some(node_archqual) = node_archqual {
2463 self.0.splice_children(
2464 node_archqual.index()..node_archqual.index() + 1,
2465 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2466 );
2467 } else {
2468 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
2469 let idx = if let Some(name_node) = name_node {
2470 name_node.index() + 1
2471 } else {
2472 0
2473 };
2474 self.0.splice_children(
2475 idx..idx,
2476 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2477 );
2478 }
2479 }
2480
2481 pub fn version(&self) -> Option<(VersionConstraint, Version)> {
2483 let vc = self.0.children().find(|n| n.kind() == VERSION);
2484 let vc = vc.as_ref()?;
2485 let constraint = vc.children().find(|n| n.kind() == CONSTRAINT);
2486
2487 let version_str: String = vc
2489 .children_with_tokens()
2490 .filter_map(|it| match it {
2491 SyntaxElement::Token(token) if token.kind() == IDENT || token.kind() == COLON => {
2492 Some(token.text().to_string())
2493 }
2494 _ => None,
2495 })
2496 .collect();
2497
2498 if let Some(constraint) = constraint {
2499 if !version_str.is_empty() {
2500 let vc: VersionConstraint = constraint.to_string().parse().unwrap();
2501 Some((vc, version_str.parse().unwrap()))
2502 } else {
2503 None
2504 }
2505 } else {
2506 None
2507 }
2508 }
2509
2510 pub fn set_version(&mut self, version_constraint: Option<(VersionConstraint, Version)>) {
2521 let current_version = self.0.children().find(|n| n.kind() == VERSION);
2522 if let Some((vc, version)) = version_constraint {
2523 let mut builder = GreenNodeBuilder::new();
2524 builder.start_node(VERSION.into());
2525 builder.token(L_PARENS.into(), "(");
2526 builder.start_node(CONSTRAINT.into());
2527 match vc {
2528 VersionConstraint::GreaterThanEqual => {
2529 builder.token(R_ANGLE.into(), ">");
2530 builder.token(EQUAL.into(), "=");
2531 }
2532 VersionConstraint::LessThanEqual => {
2533 builder.token(L_ANGLE.into(), "<");
2534 builder.token(EQUAL.into(), "=");
2535 }
2536 VersionConstraint::Equal => {
2537 builder.token(EQUAL.into(), "=");
2538 }
2539 VersionConstraint::GreaterThan => {
2540 builder.token(R_ANGLE.into(), ">");
2541 }
2542 VersionConstraint::LessThan => {
2543 builder.token(L_ANGLE.into(), "<");
2544 }
2545 }
2546 builder.finish_node(); builder.token(WHITESPACE.into(), " ");
2548 tokenize_version(&mut builder, &version);
2549 builder.token(R_PARENS.into(), ")");
2550 builder.finish_node(); if let Some(current_version) = current_version {
2553 self.0.splice_children(
2554 current_version.index()..current_version.index() + 1,
2555 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2556 );
2557 } else {
2558 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
2559 let idx = if let Some(name_node) = name_node {
2560 name_node.index() + 1
2561 } else {
2562 0
2563 };
2564 let new_children = vec![
2565 GreenToken::new(WHITESPACE.into(), " ").into(),
2566 builder.finish().into(),
2567 ];
2568 let new_root = SyntaxNode::new_root_mut(
2569 self.0.green().splice_children(idx..idx, new_children),
2570 );
2571 if let Some(parent) = self.0.parent() {
2572 parent
2573 .splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2574 self.0 = parent
2575 .children_with_tokens()
2576 .nth(self.0.index())
2577 .unwrap()
2578 .clone()
2579 .into_node()
2580 .unwrap();
2581 } else {
2582 self.0 = new_root;
2583 }
2584 }
2585 } else if let Some(current_version) = current_version {
2586 while let Some(prev) = current_version.prev_sibling_or_token() {
2588 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
2589 prev.detach();
2590 } else {
2591 break;
2592 }
2593 }
2594 current_version.detach();
2595 }
2596 }
2597
2598 pub fn architectures(&self) -> Option<impl Iterator<Item = String> + '_> {
2607 let architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES)?;
2608
2609 Some(architectures.children_with_tokens().filter_map(|node| {
2610 let token = node.as_token()?;
2611 if token.kind() == IDENT {
2612 Some(token.text().to_string())
2613 } else {
2614 None
2615 }
2616 }))
2617 }
2618
2619 pub fn profiles(&self) -> impl Iterator<Item = Vec<BuildProfile>> + '_ {
2629 let profiles = self.0.children().filter(|n| n.kind() == PROFILES);
2630
2631 profiles.map(|profile| {
2632 let mut ret = vec![];
2634 let mut current = vec![];
2635 for token in profile.children_with_tokens() {
2636 match token.kind() {
2637 WHITESPACE | NEWLINE => {
2638 if !current.is_empty() {
2639 ret.push(current.join("").parse::<BuildProfile>().unwrap());
2640 current = vec![];
2641 }
2642 }
2643 L_ANGLE | R_ANGLE => {}
2644 _ => {
2645 current.push(token.to_string());
2646 }
2647 }
2648 }
2649 if !current.is_empty() {
2650 ret.push(current.concat().parse().unwrap());
2651 }
2652 ret
2653 })
2654 }
2655
2656 pub fn remove(&mut self) {
2667 let is_first = !self
2668 .0
2669 .siblings(Direction::Prev)
2670 .skip(1)
2671 .any(|n| n.kind() == RELATION);
2672 if !is_first {
2673 while let Some(n) = self.0.prev_sibling_or_token() {
2676 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
2677 n.detach();
2678 } else if n.kind() == PIPE {
2679 n.detach();
2680 break;
2681 } else {
2682 break;
2683 }
2684 }
2685 while let Some(n) = self.0.prev_sibling_or_token() {
2686 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
2687 n.detach();
2688 } else {
2689 break;
2690 }
2691 }
2692 } else {
2693 while let Some(n) = self.0.next_sibling_or_token() {
2696 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
2697 n.detach();
2698 } else if n.kind() == PIPE {
2699 n.detach();
2700 break;
2701 } else {
2702 break;
2703 }
2704 }
2705
2706 while let Some(n) = self.0.next_sibling_or_token() {
2707 if matches!(n.kind(), WHITESPACE | NEWLINE | COMMENT) {
2708 n.detach();
2709 } else {
2710 break;
2711 }
2712 }
2713 }
2714 if let Some(mut parent) = self.0.parent().and_then(Entry::cast) {
2716 if parent.is_empty() {
2717 parent.remove();
2718 } else {
2719 self.0.detach();
2720 }
2721 } else {
2722 self.0.detach();
2723 }
2724 }
2725
2726 pub fn set_architectures<'a>(&mut self, architectures: impl Iterator<Item = &'a str>) {
2736 let mut builder = GreenNodeBuilder::new();
2737 builder.start_node(ARCHITECTURES.into());
2738 builder.token(L_BRACKET.into(), "[");
2739 for (i, arch) in architectures.enumerate() {
2740 if i > 0 {
2741 builder.token(WHITESPACE.into(), " ");
2742 }
2743 builder.token(IDENT.into(), arch);
2744 }
2745 builder.token(R_BRACKET.into(), "]");
2746 builder.finish_node();
2747
2748 let node_architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES);
2749 if let Some(node_architectures) = node_architectures {
2750 let new_root = SyntaxNode::new_root_mut(builder.finish());
2751 self.0.splice_children(
2752 node_architectures.index()..node_architectures.index() + 1,
2753 vec![new_root.into()],
2754 );
2755 } else {
2756 let profiles = self.0.children().find(|n| n.kind() == PROFILES);
2757 let idx = if let Some(profiles) = profiles {
2758 profiles.index()
2759 } else {
2760 self.0.children_with_tokens().count()
2761 };
2762 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
2763 idx..idx,
2764 vec![
2765 GreenToken::new(WHITESPACE.into(), " ").into(),
2766 builder.finish().into(),
2767 ],
2768 ));
2769 if let Some(parent) = self.0.parent() {
2770 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2771 self.0 = parent
2772 .children_with_tokens()
2773 .nth(self.0.index())
2774 .unwrap()
2775 .clone()
2776 .into_node()
2777 .unwrap();
2778 } else {
2779 self.0 = new_root;
2780 }
2781 }
2782 }
2783
2784 pub fn add_profile(&mut self, profile: &[BuildProfile]) {
2795 let mut builder = GreenNodeBuilder::new();
2796 builder.start_node(PROFILES.into());
2797 builder.token(L_ANGLE.into(), "<");
2798 for (i, profile) in profile.iter().enumerate() {
2799 if i > 0 {
2800 builder.token(WHITESPACE.into(), " ");
2801 }
2802 match profile {
2803 BuildProfile::Disabled(name) => {
2804 builder.token(NOT.into(), "!");
2805 builder.token(IDENT.into(), name.as_str());
2806 }
2807 BuildProfile::Enabled(name) => {
2808 builder.token(IDENT.into(), name.as_str());
2809 }
2810 }
2811 }
2812 builder.token(R_ANGLE.into(), ">");
2813 builder.finish_node();
2814
2815 let node_profiles = self.0.children().find(|n| n.kind() == PROFILES);
2816 if let Some(node_profiles) = node_profiles {
2817 let new_root = SyntaxNode::new_root_mut(builder.finish());
2818 self.0.splice_children(
2819 node_profiles.index()..node_profiles.index() + 1,
2820 vec![new_root.into()],
2821 );
2822 } else {
2823 let idx = self.0.children_with_tokens().count();
2824 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
2825 idx..idx,
2826 vec![
2827 GreenToken::new(WHITESPACE.into(), " ").into(),
2828 builder.finish().into(),
2829 ],
2830 ));
2831 if let Some(parent) = self.0.parent() {
2832 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2833 self.0 = parent
2834 .children_with_tokens()
2835 .nth(self.0.index())
2836 .unwrap()
2837 .clone()
2838 .into_node()
2839 .unwrap();
2840 } else {
2841 self.0 = new_root;
2842 }
2843 }
2844 }
2845
2846 pub fn build(name: &str) -> RelationBuilder {
2848 RelationBuilder::new(name)
2849 }
2850
2851 pub fn is_implied_by(&self, outer: &Relation) -> bool {
2878 if self.try_name() != outer.try_name() {
2879 return false;
2880 }
2881
2882 let inner_version = self.version();
2883 let outer_version = outer.version();
2884
2885 if inner_version.is_none() {
2887 return true;
2888 }
2889
2890 if inner_version == outer_version {
2892 return true;
2893 }
2894
2895 if outer_version.is_none() {
2897 return false;
2898 }
2899
2900 let (inner_constraint, inner_ver) = inner_version.unwrap();
2901 let (outer_constraint, outer_ver) = outer_version.unwrap();
2902
2903 use VersionConstraint::*;
2904 match inner_constraint {
2905 GreaterThanEqual => match outer_constraint {
2906 GreaterThan => outer_ver > inner_ver,
2907 GreaterThanEqual | Equal => outer_ver >= inner_ver,
2908 LessThan | LessThanEqual => false,
2909 },
2910 Equal => match outer_constraint {
2911 Equal => outer_ver == inner_ver,
2912 _ => false,
2913 },
2914 LessThan => match outer_constraint {
2915 LessThan => outer_ver <= inner_ver,
2916 LessThanEqual | Equal => outer_ver < inner_ver,
2917 GreaterThan | GreaterThanEqual => false,
2918 },
2919 LessThanEqual => match outer_constraint {
2920 LessThanEqual | Equal | LessThan => outer_ver <= inner_ver,
2921 GreaterThan | GreaterThanEqual => false,
2922 },
2923 GreaterThan => match outer_constraint {
2924 GreaterThan => outer_ver >= inner_ver,
2925 Equal | GreaterThanEqual => outer_ver > inner_ver,
2926 LessThan | LessThanEqual => false,
2927 },
2928 }
2929 }
2930}
2931
2932pub struct RelationBuilder {
2946 name: String,
2947 version_constraint: Option<(VersionConstraint, Version)>,
2948 archqual: Option<String>,
2949 architectures: Option<Vec<String>>,
2950 profiles: Vec<Vec<BuildProfile>>,
2951}
2952
2953impl RelationBuilder {
2954 fn new(name: &str) -> Self {
2956 Self {
2957 name: name.to_string(),
2958 version_constraint: None,
2959 archqual: None,
2960 architectures: None,
2961 profiles: vec![],
2962 }
2963 }
2964
2965 pub fn version_constraint(mut self, vc: VersionConstraint, version: Version) -> Self {
2967 self.version_constraint = Some((vc, version));
2968 self
2969 }
2970
2971 pub fn archqual(mut self, archqual: &str) -> Self {
2973 self.archqual = Some(archqual.to_string());
2974 self
2975 }
2976
2977 pub fn architectures(mut self, architectures: Vec<String>) -> Self {
2979 self.architectures = Some(architectures);
2980 self
2981 }
2982
2983 pub fn profiles(mut self, profiles: Vec<Vec<BuildProfile>>) -> Self {
2985 self.profiles = profiles;
2986 self
2987 }
2988
2989 pub fn add_profile(mut self, profile: Vec<BuildProfile>) -> Self {
2991 self.profiles.push(profile);
2992 self
2993 }
2994
2995 pub fn build(self) -> Relation {
2997 let mut relation = Relation::new(&self.name, self.version_constraint);
2998 if let Some(archqual) = &self.archqual {
2999 relation.set_archqual(archqual);
3000 }
3001 if let Some(architectures) = &self.architectures {
3002 relation.set_architectures(architectures.iter().map(|s| s.as_str()));
3003 }
3004 for profile in &self.profiles {
3005 relation.add_profile(profile);
3006 }
3007 relation
3008 }
3009}
3010
3011impl PartialOrd for Relation {
3012 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
3013 Some(self.cmp(other))
3014 }
3015}
3016
3017impl Eq for Relation {}
3018
3019impl Ord for Relation {
3020 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
3021 let name_cmp = self.try_name().cmp(&other.try_name());
3023 if name_cmp != std::cmp::Ordering::Equal {
3024 return name_cmp;
3025 }
3026
3027 let self_version = self.version();
3028 let other_version = other.version();
3029
3030 match (self_version, other_version) {
3031 (Some((self_vc, self_version)), Some((other_vc, other_version))) => {
3032 let vc_cmp = self_vc.cmp(&other_vc);
3033 if vc_cmp != std::cmp::Ordering::Equal {
3034 return vc_cmp;
3035 }
3036
3037 self_version.cmp(&other_version)
3038 }
3039 (Some(_), None) => std::cmp::Ordering::Greater,
3040 (None, Some(_)) => std::cmp::Ordering::Less,
3041 (None, None) => std::cmp::Ordering::Equal,
3042 }
3043 }
3044}
3045
3046impl std::str::FromStr for Relations {
3047 type Err = String;
3048
3049 fn from_str(s: &str) -> Result<Self, Self::Err> {
3050 let parse = parse(s, false);
3051 if parse.errors.is_empty() {
3052 Ok(parse.root_mut())
3053 } else {
3054 Err(parse.errors.join("\n"))
3055 }
3056 }
3057}
3058
3059impl std::str::FromStr for Entry {
3060 type Err = String;
3061
3062 fn from_str(s: &str) -> Result<Self, Self::Err> {
3063 let root: Relations = s.parse()?;
3064
3065 let mut entries = root.entries();
3066 let entry = if let Some(entry) = entries.next() {
3067 entry
3068 } else {
3069 return Err("No entry found".to_string());
3070 };
3071
3072 if entries.next().is_some() {
3073 return Err("Multiple entries found".to_string());
3074 }
3075
3076 Ok(entry)
3077 }
3078}
3079
3080impl std::str::FromStr for Relation {
3081 type Err = String;
3082
3083 fn from_str(s: &str) -> Result<Self, Self::Err> {
3084 let entry: Entry = s.parse()?;
3085
3086 let mut relations = entry.relations();
3087 let relation = if let Some(relation) = relations.next() {
3088 relation
3089 } else {
3090 return Err("No relation found".to_string());
3091 };
3092
3093 if relations.next().is_some() {
3094 return Err("Multiple relations found".to_string());
3095 }
3096
3097 Ok(relation)
3098 }
3099}
3100
3101impl From<crate::lossy::Relation> for Relation {
3102 fn from(relation: crate::lossy::Relation) -> Self {
3103 let mut builder = Relation::build(&relation.name);
3104
3105 if let Some((vc, version)) = relation.version {
3106 builder = builder.version_constraint(vc, version);
3107 }
3108
3109 if let Some(archqual) = relation.archqual {
3110 builder = builder.archqual(&archqual);
3111 }
3112
3113 if let Some(architectures) = relation.architectures {
3114 builder = builder.architectures(architectures);
3115 }
3116
3117 builder = builder.profiles(relation.profiles);
3118
3119 builder.build()
3120 }
3121}
3122
3123impl From<Relation> for crate::lossy::Relation {
3124 fn from(relation: Relation) -> Self {
3125 crate::lossy::Relation {
3126 name: relation.try_name().unwrap_or_default(),
3127 version: relation.version(),
3128 archqual: relation.archqual(),
3129 architectures: relation.architectures().map(|a| a.collect()),
3130 profiles: relation.profiles().collect(),
3131 }
3132 }
3133}
3134
3135impl From<Entry> for Vec<crate::lossy::Relation> {
3136 fn from(entry: Entry) -> Self {
3137 entry.relations().map(|r| r.into()).collect()
3138 }
3139}
3140
3141impl From<Vec<crate::lossy::Relation>> for Entry {
3142 fn from(relations: Vec<crate::lossy::Relation>) -> Self {
3143 let relations: Vec<Relation> = relations.into_iter().map(|r| r.into()).collect();
3144 Entry::from(relations)
3145 }
3146}
3147
3148#[cfg(test)]
3149mod tests {
3150 use super::*;
3151
3152 #[test]
3153 fn test_parse() {
3154 let input = "python3-dulwich";
3155 let parsed: Relations = input.parse().unwrap();
3156 assert_eq!(parsed.to_string(), input);
3157 assert_eq!(parsed.entries().count(), 1);
3158 let entry = parsed.entries().next().unwrap();
3159 assert_eq!(entry.to_string(), "python3-dulwich");
3160 assert_eq!(entry.relations().count(), 1);
3161 let relation = entry.relations().next().unwrap();
3162 assert_eq!(relation.to_string(), "python3-dulwich");
3163 assert_eq!(relation.version(), None);
3164
3165 let input = "python3-dulwich (>= 0.20.21)";
3166 let parsed: Relations = input.parse().unwrap();
3167 assert_eq!(parsed.to_string(), input);
3168 assert_eq!(parsed.entries().count(), 1);
3169 let entry = parsed.entries().next().unwrap();
3170 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3171 assert_eq!(entry.relations().count(), 1);
3172 let relation = entry.relations().next().unwrap();
3173 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
3174 assert_eq!(
3175 relation.version(),
3176 Some((
3177 VersionConstraint::GreaterThanEqual,
3178 "0.20.21".parse().unwrap()
3179 ))
3180 );
3181 }
3182
3183 #[test]
3184 fn test_multiple() {
3185 let input = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)";
3186 let parsed: Relations = input.parse().unwrap();
3187 assert_eq!(parsed.to_string(), input);
3188 assert_eq!(parsed.entries().count(), 2);
3189 let entry = parsed.entries().next().unwrap();
3190 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3191 assert_eq!(entry.relations().count(), 1);
3192 let relation = entry.relations().next().unwrap();
3193 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
3194 assert_eq!(
3195 relation.version(),
3196 Some((
3197 VersionConstraint::GreaterThanEqual,
3198 "0.20.21".parse().unwrap()
3199 ))
3200 );
3201 let entry = parsed.entries().nth(1).unwrap();
3202 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.21)");
3203 assert_eq!(entry.relations().count(), 1);
3204 let relation = entry.relations().next().unwrap();
3205 assert_eq!(relation.to_string(), "python3-dulwich (<< 0.21)");
3206 assert_eq!(
3207 relation.version(),
3208 Some((VersionConstraint::LessThan, "0.21".parse().unwrap()))
3209 );
3210 }
3211
3212 #[test]
3213 fn test_architectures() {
3214 let input = "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]";
3215 let parsed: Relations = input.parse().unwrap();
3216 assert_eq!(parsed.to_string(), input);
3217 assert_eq!(parsed.entries().count(), 1);
3218 let entry = parsed.entries().next().unwrap();
3219 assert_eq!(
3220 entry.to_string(),
3221 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
3222 );
3223 assert_eq!(entry.relations().count(), 1);
3224 let relation = entry.relations().next().unwrap();
3225 assert_eq!(
3226 relation.to_string(),
3227 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
3228 );
3229 assert_eq!(relation.version(), None);
3230 assert_eq!(
3231 relation.architectures().unwrap().collect::<Vec<_>>(),
3232 vec![
3233 "amd64", "arm64", "armhf", "i386", "mips", "mips64el", "mipsel", "ppc64el", "s390x"
3234 ]
3235 .into_iter()
3236 .map(|s| s.to_string())
3237 .collect::<Vec<_>>()
3238 );
3239 }
3240
3241 #[test]
3242 fn test_profiles() {
3243 let input = "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>, bar";
3244 let parsed: Relations = input.parse().unwrap();
3245 assert_eq!(parsed.to_string(), input);
3246 assert_eq!(parsed.entries().count(), 2);
3247 let entry = parsed.entries().next().unwrap();
3248 assert_eq!(
3249 entry.to_string(),
3250 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
3251 );
3252 assert_eq!(entry.relations().count(), 1);
3253 let relation = entry.relations().next().unwrap();
3254 assert_eq!(
3255 relation.to_string(),
3256 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
3257 );
3258 assert_eq!(
3259 relation.version(),
3260 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap()))
3261 );
3262 assert_eq!(
3263 relation.architectures().unwrap().collect::<Vec<_>>(),
3264 vec!["i386", "arm"]
3265 .into_iter()
3266 .map(|s| s.to_string())
3267 .collect::<Vec<_>>()
3268 );
3269 assert_eq!(
3270 relation.profiles().collect::<Vec<_>>(),
3271 vec![
3272 vec![BuildProfile::Disabled("nocheck".to_string())],
3273 vec![BuildProfile::Disabled("cross".to_string())]
3274 ]
3275 );
3276 }
3277
3278 #[test]
3279 fn test_substvar() {
3280 let input = "${shlibs:Depends}";
3281
3282 let (parsed, errors) = Relations::parse_relaxed(input, true);
3283 assert_eq!(errors, Vec::<String>::new());
3284 assert_eq!(parsed.to_string(), input);
3285 assert_eq!(parsed.entries().count(), 0);
3286
3287 assert_eq!(
3288 parsed.substvars().collect::<Vec<_>>(),
3289 vec!["${shlibs:Depends}"]
3290 );
3291 }
3292
3293 #[test]
3294 fn test_substvar_nodes() {
3295 let input = "foo, ${shlibs:Depends}, bar, ${misc:Depends}";
3296
3297 let (parsed, errors) = Relations::parse_relaxed(input, true);
3298 assert_eq!(errors, Vec::<String>::new());
3299
3300 let substvar_nodes: Vec<Substvar> = parsed.substvar_nodes().collect();
3301 assert_eq!(substvar_nodes.len(), 2);
3302 assert_eq!(substvar_nodes[0].to_string(), "${shlibs:Depends}");
3303 assert_eq!(substvar_nodes[1].to_string(), "${misc:Depends}");
3304 }
3305
3306 #[test]
3307 fn test_substvar_nodes_empty() {
3308 let parsed: Relations = "foo, bar".parse().unwrap();
3309
3310 let substvar_nodes: Vec<Substvar> = parsed.substvar_nodes().collect();
3311 assert_eq!(substvar_nodes.len(), 0);
3312 }
3313
3314 #[test]
3315 fn test_new() {
3316 let r = Relation::new(
3317 "samba",
3318 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
3319 );
3320
3321 assert_eq!(r.to_string(), "samba (>= 2.0)");
3322 }
3323
3324 #[test]
3325 fn test_drop_constraint() {
3326 let mut r = Relation::new(
3327 "samba",
3328 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
3329 );
3330
3331 r.drop_constraint();
3332
3333 assert_eq!(r.to_string(), "samba");
3334 }
3335
3336 #[test]
3337 fn test_simple() {
3338 let r = Relation::simple("samba");
3339
3340 assert_eq!(r.to_string(), "samba");
3341 }
3342
3343 #[test]
3344 fn test_remove_first_entry() {
3345 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3346 .parse()
3347 .unwrap();
3348 let removed = rels.remove_entry(0);
3349 assert_eq!(removed.to_string(), "python3-dulwich (>= 0.20.21)");
3350 assert_eq!(rels.to_string(), "python3-dulwich (<< 0.21)");
3351 }
3352
3353 #[test]
3354 fn test_remove_last_entry() {
3355 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3356 .parse()
3357 .unwrap();
3358 rels.remove_entry(1);
3359 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
3360 }
3361
3362 #[test]
3363 fn test_remove_middle() {
3364 let mut rels: Relations =
3365 r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21), python3-dulwich (<< 0.22)"#
3366 .parse()
3367 .unwrap();
3368 rels.remove_entry(1);
3369 assert_eq!(
3370 rels.to_string(),
3371 "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.22)"
3372 );
3373 }
3374
3375 #[test]
3376 fn test_remove_added() {
3377 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
3378 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3379 rels.push(entry);
3380 rels.remove_entry(1);
3381 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
3382 }
3383
3384 #[test]
3385 fn test_push() {
3386 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
3387 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3388 rels.push(entry);
3389 assert_eq!(
3390 rels.to_string(),
3391 "python3-dulwich (>= 0.20.21), python3-dulwich"
3392 );
3393 }
3394
3395 #[test]
3396 fn test_insert_with_custom_separator() {
3397 let mut rels: Relations = "python3".parse().unwrap();
3398 let entry = Entry::from(vec![Relation::simple("debhelper")]);
3399 rels.insert_with_separator(1, entry, Some("\n "));
3400 assert_eq!(rels.to_string(), "python3,\n debhelper");
3401 }
3402
3403 #[test]
3404 fn test_insert_with_wrap_and_sort_separator() {
3405 let mut rels: Relations = "python3".parse().unwrap();
3406 let entry = Entry::from(vec![Relation::simple("rustc")]);
3407 rels.insert_with_separator(1, entry, Some("\n "));
3409 assert_eq!(rels.to_string(), "python3,\n rustc");
3410 }
3411
3412 #[test]
3413 fn test_push_from_empty() {
3414 let mut rels: Relations = "".parse().unwrap();
3415 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3416 rels.push(entry);
3417 assert_eq!(rels.to_string(), "python3-dulwich");
3418 }
3419
3420 #[test]
3421 fn test_insert() {
3422 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3423 .parse()
3424 .unwrap();
3425 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3426 rels.insert(1, entry);
3427 assert_eq!(
3428 rels.to_string(),
3429 "python3-dulwich (>= 0.20.21), python3-dulwich, python3-dulwich (<< 0.21)"
3430 );
3431 }
3432
3433 #[test]
3434 fn test_insert_at_start() {
3435 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3436 .parse()
3437 .unwrap();
3438 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3439 rels.insert(0, entry);
3440 assert_eq!(
3441 rels.to_string(),
3442 "python3-dulwich, python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3443 );
3444 }
3445
3446 #[test]
3447 fn test_insert_after_error() {
3448 let (mut rels, errors) = Relations::parse_relaxed("@foo@, debhelper (>= 1.0)", false);
3449 assert_eq!(
3450 errors,
3451 vec![
3452 "expected $ or identifier but got ERROR",
3453 "expected comma or end of file but got Some(IDENT)",
3454 "expected $ or identifier but got ERROR"
3455 ]
3456 );
3457 let entry = Entry::from(vec![Relation::simple("bar")]);
3458 rels.push(entry);
3459 assert_eq!(rels.to_string(), "@foo@, debhelper (>= 1.0), bar");
3460 }
3461
3462 #[test]
3463 fn test_insert_before_error() {
3464 let (mut rels, errors) = Relations::parse_relaxed("debhelper (>= 1.0), @foo@, bla", false);
3465 assert_eq!(
3466 errors,
3467 vec![
3468 "expected $ or identifier but got ERROR",
3469 "expected comma or end of file but got Some(IDENT)",
3470 "expected $ or identifier but got ERROR"
3471 ]
3472 );
3473 let entry = Entry::from(vec![Relation::simple("bar")]);
3474 rels.insert(0, entry);
3475 assert_eq!(rels.to_string(), "bar, debhelper (>= 1.0), @foo@, bla");
3476 }
3477
3478 #[test]
3479 fn test_replace() {
3480 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3481 .parse()
3482 .unwrap();
3483 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3484 rels.replace(1, entry);
3485 assert_eq!(
3486 rels.to_string(),
3487 "python3-dulwich (>= 0.20.21), python3-dulwich"
3488 );
3489 }
3490
3491 #[test]
3492 fn test_relation_from_entries() {
3493 let entries = vec![
3494 Entry::from(vec![Relation::simple("python3-dulwich")]),
3495 Entry::from(vec![Relation::simple("python3-breezy")]),
3496 ];
3497 let rels: Relations = entries.into();
3498 assert_eq!(rels.entries().count(), 2);
3499 assert_eq!(rels.to_string(), "python3-dulwich, python3-breezy");
3500 }
3501
3502 #[test]
3503 fn test_entry_from_relations() {
3504 let relations = vec![
3505 Relation::simple("python3-dulwich"),
3506 Relation::simple("python3-breezy"),
3507 ];
3508 let entry: Entry = relations.into();
3509 assert_eq!(entry.relations().count(), 2);
3510 assert_eq!(entry.to_string(), "python3-dulwich | python3-breezy");
3511 }
3512
3513 #[test]
3514 fn test_parse_entry() {
3515 let parsed: Entry = "python3-dulwich (>= 0.20.21) | bar".parse().unwrap();
3516 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21) | bar");
3517 assert_eq!(parsed.relations().count(), 2);
3518
3519 assert_eq!(
3520 "foo, bar".parse::<Entry>().unwrap_err(),
3521 "Multiple entries found"
3522 );
3523 assert_eq!("".parse::<Entry>().unwrap_err(), "No entry found");
3524 }
3525
3526 #[test]
3527 fn test_parse_relation() {
3528 let parsed: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3529 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21)");
3530 assert_eq!(
3531 parsed.version(),
3532 Some((
3533 VersionConstraint::GreaterThanEqual,
3534 "0.20.21".parse().unwrap()
3535 ))
3536 );
3537 assert_eq!(
3538 "foo | bar".parse::<Relation>().unwrap_err(),
3539 "Multiple relations found"
3540 );
3541 assert_eq!("".parse::<Relation>().unwrap_err(), "No entry found");
3542 }
3543
3544 #[test]
3545 fn test_special() {
3546 let parsed: Relation = "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
3547 .parse()
3548 .unwrap();
3549 assert_eq!(
3550 parsed.to_string(),
3551 "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
3552 );
3553 assert_eq!(
3554 parsed.version(),
3555 Some((
3556 VersionConstraint::GreaterThanEqual,
3557 "0.1.138-~~".parse().unwrap()
3558 ))
3559 );
3560 assert_eq!(parsed.archqual(), Some("amd64".to_string()));
3561 assert_eq!(parsed.name(), "librust-breezyshim+dirty-tracker-dev");
3562 }
3563
3564 #[test]
3565 fn test_relations_satisfied_by() {
3566 let rels: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3567 .parse()
3568 .unwrap();
3569 let satisfied = |name: &str| -> Option<debversion::Version> {
3570 match name {
3571 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
3572 _ => None,
3573 }
3574 };
3575 assert!(rels.satisfied_by(satisfied));
3576
3577 let satisfied = |name: &str| match name {
3578 "python3-dulwich" => Some("0.21".parse().unwrap()),
3579 _ => None,
3580 };
3581 assert!(!rels.satisfied_by(satisfied));
3582
3583 let satisfied = |name: &str| match name {
3584 "python3-dulwich" => Some("0.20.20".parse().unwrap()),
3585 _ => None,
3586 };
3587 assert!(!rels.satisfied_by(satisfied));
3588 }
3589
3590 #[test]
3591 fn test_entry_satisfied_by() {
3592 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3593 .parse()
3594 .unwrap();
3595 let satisfied = |name: &str| -> Option<debversion::Version> {
3596 match name {
3597 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
3598 _ => None,
3599 }
3600 };
3601 assert!(entry.satisfied_by(satisfied));
3602 let satisfied = |name: &str| -> Option<debversion::Version> {
3603 match name {
3604 "python3-dulwich" => Some("0.18".parse().unwrap()),
3605 _ => None,
3606 }
3607 };
3608 assert!(!entry.satisfied_by(satisfied));
3609 }
3610
3611 #[test]
3612 fn test_wrap_and_sort_relation() {
3613 let relation: Relation = " python3-dulwich (>= 11) [ amd64 ] < lala>"
3614 .parse()
3615 .unwrap();
3616
3617 let wrapped = relation.wrap_and_sort();
3618
3619 assert_eq!(
3620 wrapped.to_string(),
3621 "python3-dulwich (>= 11) [amd64] <lala>"
3622 );
3623 }
3624
3625 #[test]
3626 fn test_wrap_and_sort_relations() {
3627 let entry: Relations =
3628 "python3-dulwich (>= 0.20.21) | bar, \n\n\n\npython3-dulwich (<< 0.21)"
3629 .parse()
3630 .unwrap();
3631
3632 let wrapped = entry.wrap_and_sort();
3633
3634 assert_eq!(
3635 wrapped.to_string(),
3636 "bar | python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3637 );
3638 }
3639
3640 #[cfg(feature = "serde")]
3641 #[test]
3642 fn test_serialize_relations() {
3643 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3644 .parse()
3645 .unwrap();
3646 let serialized = serde_json::to_string(&relations).unwrap();
3647 assert_eq!(
3648 serialized,
3649 r#""python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)""#
3650 );
3651 }
3652
3653 #[cfg(feature = "serde")]
3654 #[test]
3655 fn test_deserialize_relations() {
3656 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3657 .parse()
3658 .unwrap();
3659 let serialized = serde_json::to_string(&relations).unwrap();
3660 let deserialized: Relations = serde_json::from_str(&serialized).unwrap();
3661 assert_eq!(deserialized.to_string(), relations.to_string());
3662 }
3663
3664 #[cfg(feature = "serde")]
3665 #[test]
3666 fn test_serialize_relation() {
3667 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3668 let serialized = serde_json::to_string(&relation).unwrap();
3669 assert_eq!(serialized, r#""python3-dulwich (>= 0.20.21)""#);
3670 }
3671
3672 #[cfg(feature = "serde")]
3673 #[test]
3674 fn test_deserialize_relation() {
3675 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3676 let serialized = serde_json::to_string(&relation).unwrap();
3677 let deserialized: Relation = serde_json::from_str(&serialized).unwrap();
3678 assert_eq!(deserialized.to_string(), relation.to_string());
3679 }
3680
3681 #[cfg(feature = "serde")]
3682 #[test]
3683 fn test_serialize_entry() {
3684 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3685 .parse()
3686 .unwrap();
3687 let serialized = serde_json::to_string(&entry).unwrap();
3688 assert_eq!(
3689 serialized,
3690 r#""python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)""#
3691 );
3692 }
3693
3694 #[cfg(feature = "serde")]
3695 #[test]
3696 fn test_deserialize_entry() {
3697 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3698 .parse()
3699 .unwrap();
3700 let serialized = serde_json::to_string(&entry).unwrap();
3701 let deserialized: Entry = serde_json::from_str(&serialized).unwrap();
3702 assert_eq!(deserialized.to_string(), entry.to_string());
3703 }
3704
3705 #[test]
3706 fn test_remove_first_relation() {
3707 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3708 .parse()
3709 .unwrap();
3710 let mut rel = entry.relations().next().unwrap();
3711 rel.remove();
3712 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.18)");
3713 }
3714
3715 #[test]
3716 fn test_remove_last_relation() {
3717 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3718 .parse()
3719 .unwrap();
3720 let mut rel = entry.relations().nth(1).unwrap();
3721 rel.remove();
3722 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3723 }
3724
3725 #[test]
3726 fn test_remove_only_relation() {
3727 let entry: Entry = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3728 let mut rel = entry.relations().next().unwrap();
3729 rel.remove();
3730 assert_eq!(entry.to_string(), "");
3731 }
3732
3733 #[test]
3734 fn test_relations_is_empty() {
3735 let entry: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3736 assert!(!entry.is_empty());
3737 assert_eq!(1, entry.len());
3738 let mut rel = entry.entries().next().unwrap();
3739 rel.remove();
3740 assert!(entry.is_empty());
3741 assert_eq!(0, entry.len());
3742 }
3743
3744 #[test]
3745 fn test_entry_is_empty() {
3746 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3747 .parse()
3748 .unwrap();
3749 assert!(!entry.is_empty());
3750 assert_eq!(2, entry.len());
3751 let mut rel = entry.relations().next().unwrap();
3752 rel.remove();
3753 assert!(!entry.is_empty());
3754 assert_eq!(1, entry.len());
3755 let mut rel = entry.relations().next().unwrap();
3756 rel.remove();
3757 assert!(entry.is_empty());
3758 assert_eq!(0, entry.len());
3759 }
3760
3761 #[test]
3762 fn test_relation_set_version() {
3763 let mut rel: Relation = "samba".parse().unwrap();
3764 rel.set_version(None);
3765 assert_eq!("samba", rel.to_string());
3766 rel.set_version(Some((
3767 VersionConstraint::GreaterThanEqual,
3768 "2.0".parse().unwrap(),
3769 )));
3770 assert_eq!("samba (>= 2.0)", rel.to_string());
3771 rel.set_version(None);
3772 assert_eq!("samba", rel.to_string());
3773 rel.set_version(Some((
3774 VersionConstraint::GreaterThanEqual,
3775 "2.0".parse().unwrap(),
3776 )));
3777 rel.set_version(Some((
3778 VersionConstraint::GreaterThanEqual,
3779 "1.1".parse().unwrap(),
3780 )));
3781 assert_eq!("samba (>= 1.1)", rel.to_string());
3782 }
3783
3784 #[test]
3785 fn test_replace_relation() {
3786 let mut entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3787 .parse()
3788 .unwrap();
3789 let new_rel = Relation::simple("python3-breezy");
3790 entry.replace(0, new_rel);
3791 assert_eq!(
3792 entry.to_string(),
3793 "python3-breezy | python3-dulwich (<< 0.18)"
3794 );
3795 }
3796
3797 #[test]
3798 fn test_entry_push_relation() {
3799 let relations: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3800 let new_rel = Relation::simple("python3-breezy");
3801 let mut entry = relations.entries().next().unwrap();
3802 entry.push(new_rel);
3803 assert_eq!(
3804 entry.to_string(),
3805 "python3-dulwich (>= 0.20.21) | python3-breezy"
3806 );
3807 assert_eq!(
3808 relations.to_string(),
3809 "python3-dulwich (>= 0.20.21) | python3-breezy"
3810 );
3811 }
3812
3813 #[test]
3814 fn test_relations_remove_empty_entry() {
3815 let (mut relations, errors) = Relations::parse_relaxed("foo, , bar, ", false);
3816 assert_eq!(errors, Vec::<String>::new());
3817 assert_eq!(relations.to_string(), "foo, , bar, ");
3818 assert_eq!(relations.len(), 2);
3819 assert_eq!(
3820 relations.entries().next().unwrap().to_string(),
3821 "foo".to_string()
3822 );
3823 assert_eq!(
3824 relations.entries().nth(1).unwrap().to_string(),
3825 "bar".to_string()
3826 );
3827 relations.remove_entry(1);
3828 assert_eq!(relations.to_string(), "foo, , ");
3829 }
3830
3831 #[test]
3832 fn test_entry_remove_relation() {
3833 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
3834 let removed = entry.remove_relation(0);
3835 assert_eq!(removed.to_string(), "python3-dulwich");
3836 assert_eq!(entry.to_string(), "samba");
3837 }
3838
3839 #[test]
3840 fn test_wrap_and_sort_removes_empty_entries() {
3841 let relations: Relations = "foo, , bar, ".parse().unwrap();
3842 let wrapped = relations.wrap_and_sort();
3843 assert_eq!(wrapped.to_string(), "bar, foo");
3844 }
3845
3846 #[test]
3847 fn test_set_archqual() {
3848 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
3849 let mut rel = entry.relations().next().unwrap();
3850 rel.set_archqual("amd64");
3851 assert_eq!(rel.to_string(), "python3-dulwich:amd64");
3852 assert_eq!(rel.archqual(), Some("amd64".to_string()));
3853 assert_eq!(entry.to_string(), "python3-dulwich:amd64 | samba");
3854 rel.set_archqual("i386");
3855 assert_eq!(rel.to_string(), "python3-dulwich:i386");
3856 assert_eq!(rel.archqual(), Some("i386".to_string()));
3857 assert_eq!(entry.to_string(), "python3-dulwich:i386 | samba");
3858 }
3859
3860 #[test]
3861 fn test_set_architectures() {
3862 let mut relation = Relation::simple("samba");
3863 relation.set_architectures(vec!["amd64", "i386"].into_iter());
3864 assert_eq!(relation.to_string(), "samba [amd64 i386]");
3865 }
3866
3867 #[test]
3868 fn test_relation_builder_no_architectures() {
3869 let relation = Relation::build("debhelper").build();
3871 assert_eq!(relation.to_string(), "debhelper");
3872 }
3873
3874 #[test]
3875 fn test_relation_builder_with_architectures() {
3876 let relation = Relation::build("samba")
3878 .architectures(vec!["amd64".to_string(), "i386".to_string()])
3879 .build();
3880 assert_eq!(relation.to_string(), "samba [amd64 i386]");
3881 }
3882
3883 #[test]
3884 fn test_ensure_minimum_version_add_new() {
3885 let mut relations: Relations = "python3".parse().unwrap();
3886 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3887 assert_eq!(relations.to_string(), "python3, debhelper (>= 12)");
3888 }
3889
3890 #[test]
3891 fn test_ensure_minimum_version_update() {
3892 let mut relations: Relations = "debhelper (>= 9)".parse().unwrap();
3893 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3894 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3895 }
3896
3897 #[test]
3898 fn test_ensure_minimum_version_no_change() {
3899 let mut relations: Relations = "debhelper (>= 13)".parse().unwrap();
3900 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3901 assert_eq!(relations.to_string(), "debhelper (>= 13)");
3902 }
3903
3904 #[test]
3905 fn test_ensure_minimum_version_no_version() {
3906 let mut relations: Relations = "debhelper".parse().unwrap();
3907 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3908 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3909 }
3910
3911 #[test]
3912 fn test_ensure_minimum_version_preserves_newline() {
3913 let input = "\n debhelper (>= 9),\n pkg-config,\n uuid-dev";
3919 let mut relations: Relations = input.parse().unwrap();
3920 relations.ensure_minimum_version("debhelper", &"12~".parse().unwrap());
3921 let result = relations.to_string();
3922
3923 assert!(
3925 result.starts_with('\n'),
3926 "Expected result to start with newline, got: {:?}",
3927 result
3928 );
3929 assert_eq!(result, "\n debhelper (>= 12~),\n pkg-config,\n uuid-dev");
3930 }
3931
3932 #[test]
3933 fn test_ensure_minimum_version_preserves_newline_in_control() {
3934 use crate::lossless::Control;
3936 use std::str::FromStr;
3937
3938 let input = r#"Source: f2fs-tools
3939Section: admin
3940Priority: optional
3941Maintainer: Test <test@example.com>
3942Build-Depends:
3943 debhelper (>= 9),
3944 pkg-config,
3945 uuid-dev
3946
3947Package: f2fs-tools
3948Description: test
3949"#;
3950
3951 let control = Control::from_str(input).unwrap();
3952 let mut source = control.source().unwrap();
3953 let mut build_depends = source.build_depends().unwrap();
3954
3955 let version = Version::from_str("12~").unwrap();
3956 build_depends.ensure_minimum_version("debhelper", &version);
3957
3958 source.set_build_depends(&build_depends);
3959
3960 let output = control.to_string();
3961
3962 assert!(
3964 output.contains("Build-Depends:\n debhelper (>= 12~)"),
3965 "Expected 'Build-Depends:\\n debhelper (>= 12~)' but got:\n{}",
3966 output
3967 );
3968 }
3969
3970 #[test]
3971 fn test_ensure_exact_version_add_new() {
3972 let mut relations: Relations = "python3".parse().unwrap();
3973 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3974 assert_eq!(relations.to_string(), "python3, debhelper (= 12)");
3975 }
3976
3977 #[test]
3978 fn test_ensure_exact_version_update() {
3979 let mut relations: Relations = "debhelper (>= 9)".parse().unwrap();
3980 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3981 assert_eq!(relations.to_string(), "debhelper (= 12)");
3982 }
3983
3984 #[test]
3985 fn test_ensure_exact_version_no_change() {
3986 let mut relations: Relations = "debhelper (= 12)".parse().unwrap();
3987 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3988 assert_eq!(relations.to_string(), "debhelper (= 12)");
3989 }
3990
3991 #[test]
3992 fn test_ensure_some_version_add_new() {
3993 let mut relations: Relations = "python3".parse().unwrap();
3994 relations.ensure_some_version("debhelper");
3995 assert_eq!(relations.to_string(), "python3, debhelper");
3996 }
3997
3998 #[test]
3999 fn test_ensure_some_version_exists_with_version() {
4000 let mut relations: Relations = "debhelper (>= 12)".parse().unwrap();
4001 relations.ensure_some_version("debhelper");
4002 assert_eq!(relations.to_string(), "debhelper (>= 12)");
4003 }
4004
4005 #[test]
4006 fn test_ensure_some_version_exists_no_version() {
4007 let mut relations: Relations = "debhelper".parse().unwrap();
4008 relations.ensure_some_version("debhelper");
4009 assert_eq!(relations.to_string(), "debhelper");
4010 }
4011
4012 #[test]
4013 fn test_ensure_substvar() {
4014 let mut relations: Relations = "python3".parse().unwrap();
4015 relations.ensure_substvar("${misc:Depends}").unwrap();
4016 assert_eq!(relations.to_string(), "python3, ${misc:Depends}");
4017 }
4018
4019 #[test]
4020 fn test_ensure_substvar_already_exists() {
4021 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
4022 relations.ensure_substvar("${misc:Depends}").unwrap();
4023 assert_eq!(relations.to_string(), "python3, ${misc:Depends}");
4024 }
4025
4026 #[test]
4027 fn test_ensure_substvar_empty_relations() {
4028 let mut relations: Relations = Relations::new();
4029 relations.ensure_substvar("${misc:Depends}").unwrap();
4030 assert_eq!(relations.to_string(), "${misc:Depends}");
4031 }
4032
4033 #[test]
4034 fn test_ensure_substvar_preserves_whitespace() {
4035 let (mut relations, _) = Relations::parse_relaxed("python3, rustc", false);
4037 relations.ensure_substvar("${misc:Depends}").unwrap();
4038 assert_eq!(relations.to_string(), "python3, rustc, ${misc:Depends}");
4040 }
4041
4042 #[test]
4043 fn test_ensure_substvar_to_existing_substvar() {
4044 let (mut relations, _) = Relations::parse_relaxed("${shlibs:Depends}", true);
4047 relations.ensure_substvar("${misc:Depends}").unwrap();
4048 assert_eq!(relations.to_string(), "${shlibs:Depends}, ${misc:Depends}");
4050 }
4051
4052 #[test]
4053 fn test_drop_substvar_basic() {
4054 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
4055 relations.drop_substvar("${misc:Depends}");
4056 assert_eq!(relations.to_string(), "python3");
4057 }
4058
4059 #[test]
4060 fn test_drop_substvar_first_position() {
4061 let (mut relations, _) = Relations::parse_relaxed("${misc:Depends}, python3", true);
4062 relations.drop_substvar("${misc:Depends}");
4063 assert_eq!(relations.to_string(), "python3");
4064 }
4065
4066 #[test]
4067 fn test_drop_substvar_middle_position() {
4068 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}, rustc", true);
4069 relations.drop_substvar("${misc:Depends}");
4070 assert_eq!(relations.to_string(), "python3, rustc");
4071 }
4072
4073 #[test]
4074 fn test_drop_substvar_only_substvar() {
4075 let (mut relations, _) = Relations::parse_relaxed("${misc:Depends}", true);
4076 relations.drop_substvar("${misc:Depends}");
4077 assert_eq!(relations.to_string(), "");
4078 }
4079
4080 #[test]
4081 fn test_drop_substvar_not_exists() {
4082 let (mut relations, _) = Relations::parse_relaxed("python3, rustc", false);
4083 relations.drop_substvar("${misc:Depends}");
4084 assert_eq!(relations.to_string(), "python3, rustc");
4085 }
4086
4087 #[test]
4088 fn test_drop_substvar_multiple_substvars() {
4089 let (mut relations, _) =
4090 Relations::parse_relaxed("python3, ${misc:Depends}, ${shlibs:Depends}", true);
4091 relations.drop_substvar("${misc:Depends}");
4092 assert_eq!(relations.to_string(), "python3, ${shlibs:Depends}");
4093 }
4094
4095 #[test]
4096 fn test_drop_substvar_preserves_whitespace() {
4097 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
4098 relations.drop_substvar("${misc:Depends}");
4099 assert_eq!(relations.to_string(), "python3");
4100 }
4101
4102 #[test]
4103 fn test_filter_entries_basic() {
4104 let mut relations: Relations = "python3, debhelper, rustc".parse().unwrap();
4105 relations.filter_entries(|entry| entry.relations().any(|r| r.name().starts_with("python")));
4106 assert_eq!(relations.to_string(), "python3");
4107 }
4108
4109 #[test]
4110 fn test_filter_entries_keep_all() {
4111 let mut relations: Relations = "python3, debhelper".parse().unwrap();
4112 relations.filter_entries(|_| true);
4113 assert_eq!(relations.to_string(), "python3, debhelper");
4114 }
4115
4116 #[test]
4117 fn test_filter_entries_remove_all() {
4118 let mut relations: Relations = "python3, debhelper".parse().unwrap();
4119 relations.filter_entries(|_| false);
4120 assert_eq!(relations.to_string(), "");
4121 }
4122
4123 #[test]
4124 fn test_filter_entries_keep_middle() {
4125 let mut relations: Relations = "aaa, bbb, ccc".parse().unwrap();
4126 relations.filter_entries(|entry| entry.relations().any(|r| r.name() == "bbb"));
4127 assert_eq!(relations.to_string(), "bbb");
4128 }
4129
4130 #[test]
4133 fn test_is_sorted_wrap_and_sort_order() {
4134 let relations: Relations = "debhelper, python3, rustc".parse().unwrap();
4136 assert!(relations.is_sorted(&WrapAndSortOrder));
4137
4138 let relations: Relations = "rustc, debhelper, python3".parse().unwrap();
4140 assert!(!relations.is_sorted(&WrapAndSortOrder));
4141
4142 let (relations, _) =
4144 Relations::parse_relaxed("cdbs, debhelper-compat, python3, ${misc:Depends}", true);
4145 assert!(relations.is_sorted(&WrapAndSortOrder));
4146 }
4147
4148 #[test]
4149 fn test_is_sorted_default_order() {
4150 let relations: Relations = "aaa, bbb, ccc".parse().unwrap();
4152 assert!(relations.is_sorted(&DefaultSortingOrder));
4153
4154 let relations: Relations = "ccc, aaa, bbb".parse().unwrap();
4156 assert!(!relations.is_sorted(&DefaultSortingOrder));
4157
4158 let (relations, _) = Relations::parse_relaxed("aaa, bbb, ${misc:Depends}", true);
4160 assert!(relations.is_sorted(&DefaultSortingOrder));
4161 }
4162
4163 #[test]
4164 fn test_is_sorted_with_substvars() {
4165 let (relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}, rustc", true);
4167 assert!(relations.is_sorted(&DefaultSortingOrder));
4169 }
4170
4171 #[test]
4172 fn test_drop_dependency_exists() {
4173 let mut relations: Relations = "python3, debhelper, rustc".parse().unwrap();
4174 assert!(relations.drop_dependency("debhelper"));
4175 assert_eq!(relations.to_string(), "python3, rustc");
4176 }
4177
4178 #[test]
4179 fn test_drop_dependency_not_exists() {
4180 let mut relations: Relations = "python3, rustc".parse().unwrap();
4181 assert!(!relations.drop_dependency("nonexistent"));
4182 assert_eq!(relations.to_string(), "python3, rustc");
4183 }
4184
4185 #[test]
4186 fn test_drop_dependency_only_item() {
4187 let mut relations: Relations = "python3".parse().unwrap();
4188 assert!(relations.drop_dependency("python3"));
4189 assert_eq!(relations.to_string(), "");
4190 }
4191
4192 #[test]
4193 fn test_add_dependency_to_empty() {
4194 let mut relations: Relations = "".parse().unwrap();
4195 let entry = Entry::from(Relation::simple("python3"));
4196 relations.add_dependency(entry, None);
4197 assert_eq!(relations.to_string(), "python3");
4198 }
4199
4200 #[test]
4201 fn test_add_dependency_sorted_position() {
4202 let mut relations: Relations = "debhelper, rustc".parse().unwrap();
4203 let entry = Entry::from(Relation::simple("python3"));
4204 relations.add_dependency(entry, None);
4205 assert_eq!(relations.to_string(), "debhelper, python3, rustc");
4207 }
4208
4209 #[test]
4210 fn test_add_dependency_explicit_position() {
4211 let mut relations: Relations = "python3, rustc".parse().unwrap();
4212 let entry = Entry::from(Relation::simple("debhelper"));
4213 relations.add_dependency(entry, Some(0));
4214 assert_eq!(relations.to_string(), "debhelper, python3, rustc");
4215 }
4216
4217 #[test]
4218 fn test_add_dependency_build_system_first() {
4219 let mut relations: Relations = "python3, rustc".parse().unwrap();
4220 let entry = Entry::from(Relation::simple("debhelper-compat"));
4221 relations.add_dependency(entry, None);
4222 assert_eq!(relations.to_string(), "debhelper-compat, python3, rustc");
4224 }
4225
4226 #[test]
4227 fn test_add_dependency_at_end() {
4228 let mut relations: Relations = "debhelper, python3".parse().unwrap();
4229 let entry = Entry::from(Relation::simple("zzz-package"));
4230 relations.add_dependency(entry, None);
4231 assert_eq!(relations.to_string(), "debhelper, python3, zzz-package");
4233 }
4234
4235 #[test]
4236 fn test_add_dependency_to_single_entry() {
4237 let mut relations: Relations = "python3-dulwich".parse().unwrap();
4239 let entry: Entry = "debhelper-compat (= 12)".parse().unwrap();
4240 relations.add_dependency(entry, None);
4241 assert_eq!(
4243 relations.to_string(),
4244 "debhelper-compat (= 12), python3-dulwich"
4245 );
4246 }
4247
4248 #[test]
4249 fn test_get_relation_exists() {
4250 let relations: Relations = "python3, debhelper (>= 12), rustc".parse().unwrap();
4251 let result = relations.get_relation("debhelper");
4252 assert!(result.is_ok());
4253 let (idx, entry) = result.unwrap();
4254 assert_eq!(idx, 1);
4255 assert_eq!(entry.to_string(), "debhelper (>= 12)");
4256 }
4257
4258 #[test]
4259 fn test_get_relation_not_exists() {
4260 let relations: Relations = "python3, rustc".parse().unwrap();
4261 let result = relations.get_relation("nonexistent");
4262 assert_eq!(result, Err("Package nonexistent not found".to_string()));
4263 }
4264
4265 #[test]
4266 fn test_get_relation_complex_rule() {
4267 let relations: Relations = "python3 | python3-minimal, rustc".parse().unwrap();
4268 let result = relations.get_relation("python3");
4269 assert_eq!(
4270 result,
4271 Err("Complex rule for python3, aborting".to_string())
4272 );
4273 }
4274
4275 #[test]
4276 fn test_iter_relations_for_simple() {
4277 let relations: Relations = "python3, debhelper, python3-dev".parse().unwrap();
4278 let entries: Vec<_> = relations.iter_relations_for("python3").collect();
4279 assert_eq!(entries.len(), 1);
4280 assert_eq!(entries[0].0, 0);
4281 assert_eq!(entries[0].1.to_string(), "python3");
4282 }
4283
4284 #[test]
4285 fn test_iter_relations_for_alternatives() {
4286 let relations: Relations = "python3 | python3-minimal, python3-dev".parse().unwrap();
4287 let entries: Vec<_> = relations.iter_relations_for("python3").collect();
4288 assert_eq!(entries.len(), 1);
4290 assert_eq!(entries[0].0, 0);
4291 }
4292
4293 #[test]
4294 fn test_iter_relations_for_not_found() {
4295 let relations: Relations = "python3, rustc".parse().unwrap();
4296 let entries: Vec<_> = relations.iter_relations_for("debhelper").collect();
4297 assert_eq!(entries.len(), 0);
4298 }
4299
4300 #[test]
4301 fn test_has_relation_exists() {
4302 let relations: Relations = "python3, debhelper, rustc".parse().unwrap();
4303 assert!(relations.has_relation("debhelper"));
4304 assert!(relations.has_relation("python3"));
4305 assert!(relations.has_relation("rustc"));
4306 }
4307
4308 #[test]
4309 fn test_has_relation_not_exists() {
4310 let relations: Relations = "python3, rustc".parse().unwrap();
4311 assert!(!relations.has_relation("debhelper"));
4312 }
4313
4314 #[test]
4315 fn test_has_relation_in_alternative() {
4316 let relations: Relations = "python3 | python3-minimal".parse().unwrap();
4317 assert!(relations.has_relation("python3"));
4318 assert!(relations.has_relation("python3-minimal"));
4319 }
4320
4321 #[test]
4322 fn test_sorting_order_wrap_and_sort_build_systems() {
4323 let order = WrapAndSortOrder;
4324 assert!(order.lt("debhelper", "python3"));
4326 assert!(order.lt("debhelper-compat", "rustc"));
4327 assert!(order.lt("cdbs", "aaa"));
4328 assert!(order.lt("dh-python", "python3"));
4329 }
4330
4331 #[test]
4332 fn test_sorting_order_wrap_and_sort_regular_packages() {
4333 let order = WrapAndSortOrder;
4334 assert!(order.lt("aaa", "bbb"));
4336 assert!(order.lt("python3", "rustc"));
4337 assert!(!order.lt("rustc", "python3"));
4338 }
4339
4340 #[test]
4341 fn test_sorting_order_wrap_and_sort_substvars() {
4342 let order = WrapAndSortOrder;
4343 assert!(order.lt("python3", "${misc:Depends}"));
4345 assert!(!order.lt("${misc:Depends}", "python3"));
4346 assert!(!order.ignore("${misc:Depends}"));
4348 }
4349
4350 #[test]
4351 fn test_sorting_order_default_special_items() {
4352 let order = DefaultSortingOrder;
4353 assert!(order.lt("python3", "${misc:Depends}"));
4355 assert!(order.lt("aaa", "@cdbs@"));
4356 assert!(order.ignore("${misc:Depends}"));
4358 assert!(order.ignore("@cdbs@"));
4359 assert!(!order.ignore("python3"));
4360 }
4361
4362 #[test]
4363 fn test_is_special_package_name() {
4364 assert!(is_special_package_name("${misc:Depends}"));
4365 assert!(is_special_package_name("${shlibs:Depends}"));
4366 assert!(is_special_package_name("@cdbs@"));
4367 assert!(!is_special_package_name("python3"));
4368 assert!(!is_special_package_name("debhelper"));
4369 }
4370
4371 #[test]
4372 fn test_add_dependency_with_explicit_position() {
4373 let mut relations: Relations = "python3, rustc".parse().unwrap();
4375 let entry = Entry::from(Relation::simple("debhelper"));
4376 relations.add_dependency(entry, Some(1));
4377 assert_eq!(relations.to_string(), "python3, debhelper, rustc");
4379 }
4380
4381 #[test]
4382 fn test_whitespace_detection_single_space() {
4383 let mut relations: Relations = "python3, rustc".parse().unwrap();
4384 let entry = Entry::from(Relation::simple("debhelper"));
4385 relations.add_dependency(entry, Some(1));
4386 assert_eq!(relations.to_string(), "python3, debhelper, rustc");
4387 }
4388
4389 #[test]
4390 fn test_whitespace_detection_multiple_spaces() {
4391 let mut relations: Relations = "python3, rustc, gcc".parse().unwrap();
4392 let entry = Entry::from(Relation::simple("debhelper"));
4393 relations.add_dependency(entry, Some(1));
4394 assert_eq!(relations.to_string(), "python3, debhelper, rustc, gcc");
4396 }
4397
4398 #[test]
4399 fn test_whitespace_detection_mixed_patterns() {
4400 let mut relations: Relations = "a, b, c, d, e".parse().unwrap();
4402 let entry = Entry::from(Relation::simple("x"));
4403 relations.push(entry);
4404 assert_eq!(relations.to_string(), "a, b, c, d, e, x");
4407 }
4408
4409 #[test]
4410 fn test_whitespace_detection_newlines() {
4411 let mut relations: Relations = "python3,\n rustc".parse().unwrap();
4412 let entry = Entry::from(Relation::simple("debhelper"));
4413 relations.add_dependency(entry, Some(1));
4414 assert_eq!(relations.to_string(), "python3,\n debhelper,\n rustc");
4416 }
4417
4418 #[test]
4419 fn test_append_with_newline_no_trailing() {
4420 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4421 let entry = Entry::from(Relation::simple("blah"));
4422 relations.add_dependency(entry, None);
4423 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4424 }
4425
4426 #[test]
4427 fn test_append_with_trailing_newline() {
4428 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4429 let entry = Entry::from(Relation::simple("blah"));
4430 relations.add_dependency(entry, None);
4431 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4432 }
4433
4434 #[test]
4435 fn test_append_with_4_space_indent() {
4436 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4437 let entry = Entry::from(Relation::simple("blah"));
4438 relations.add_dependency(entry, None);
4439 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4440 }
4441
4442 #[test]
4443 fn test_append_with_4_space_and_trailing_newline() {
4444 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4445 let entry = Entry::from(Relation::simple("blah"));
4446 relations.add_dependency(entry, None);
4447 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
4448 }
4449
4450 #[test]
4451 fn test_odd_syntax_append_no_trailing() {
4452 let mut relations: Relations = "\n foo\n , bar".parse().unwrap();
4453 let entry = Entry::from(Relation::simple("blah"));
4454 relations.add_dependency(entry, None);
4455 assert_eq!(relations.to_string(), "\n foo\n , bar\n , blah");
4456 }
4457
4458 #[test]
4459 fn test_odd_syntax_append_with_trailing() {
4460 let mut relations: Relations = "\n foo\n , bar\n".parse().unwrap();
4461 let entry = Entry::from(Relation::simple("blah"));
4462 relations.add_dependency(entry, None);
4463 assert_eq!(relations.to_string(), "\n foo\n , bar\n , blah");
4464 }
4465
4466 #[test]
4467 fn test_insert_at_1_no_trailing() {
4468 let mut relations: Relations = "foo,\n bar".parse().unwrap();
4469 let entry = Entry::from(Relation::simple("blah"));
4470 relations.add_dependency(entry, Some(1));
4471 assert_eq!(relations.to_string(), "foo,\n blah,\n bar");
4472 }
4473
4474 #[test]
4475 fn test_insert_at_1_with_trailing() {
4476 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
4477 let entry = Entry::from(Relation::simple("blah"));
4478 relations.add_dependency(entry, Some(1));
4479 assert_eq!(relations.to_string(), "foo,\n blah,\n bar");
4480 }
4481
4482 #[test]
4483 fn test_odd_syntax_insert_at_1() {
4484 let mut relations: Relations = "\n foo\n , bar\n".parse().unwrap();
4485 let entry = Entry::from(Relation::simple("blah"));
4486 relations.add_dependency(entry, Some(1));
4487 assert_eq!(relations.to_string(), "\n foo\n , blah\n , bar");
4488 }
4489
4490 #[test]
4491 fn test_relations_preserves_exact_whitespace() {
4492 let input =
4494 "debhelper (>= 10), quilt (>= 0.40),\n libsystemd-dev [linux-any], pkg-config";
4495
4496 let relations: Relations = input.parse().unwrap();
4497
4498 assert_eq!(
4500 relations.to_string(),
4501 input,
4502 "Relations should preserve exact whitespace from input"
4503 );
4504 }
4505
4506 #[test]
4507 fn test_remove_entry_preserves_indentation() {
4508 let input = "debhelper (>= 10), quilt (>= 0.40),\n libsystemd-dev [linux-any], dh-systemd (>= 1.5), pkg-config";
4510
4511 let mut relations: Relations = input.parse().unwrap();
4512
4513 let mut to_remove = Vec::new();
4515 for (idx, entry) in relations.entries().enumerate() {
4516 for relation in entry.relations() {
4517 if relation.name() == "dh-systemd" {
4518 to_remove.push(idx);
4519 break;
4520 }
4521 }
4522 }
4523
4524 for idx in to_remove.into_iter().rev() {
4525 relations.remove_entry(idx);
4526 }
4527
4528 let output = relations.to_string();
4529 println!("After removal: '{}'", output);
4530
4531 assert!(
4533 output.contains("\n libsystemd-dev"),
4534 "Expected 4-space indentation to be preserved, but got:\n'{}'",
4535 output
4536 );
4537 }
4538
4539 #[test]
4540 fn test_relation_is_implied_by_same_package() {
4541 let inner = Relation::new(
4543 "pkg",
4544 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4545 );
4546 let outer = Relation::new(
4547 "pkg",
4548 Some((VersionConstraint::GreaterThanEqual, "1.5".parse().unwrap())),
4549 );
4550 assert!(inner.is_implied_by(&outer));
4551 }
4552
4553 #[test]
4554 fn test_relation_is_implied_by_different_package() {
4555 let inner = Relation::new("pkg1", None);
4557 let outer = Relation::new("pkg2", None);
4558 assert!(!inner.is_implied_by(&outer));
4559 }
4560
4561 #[test]
4562 fn test_relation_is_implied_by_no_version() {
4563 let inner = Relation::new("pkg", None);
4565 let outer = Relation::new(
4566 "pkg",
4567 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4568 );
4569 assert!(inner.is_implied_by(&outer));
4570 }
4571
4572 #[test]
4573 fn test_relation_is_implied_by_identical() {
4574 let inner = Relation::new(
4576 "pkg",
4577 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4578 );
4579 let outer = Relation::new(
4580 "pkg",
4581 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4582 );
4583 assert!(inner.is_implied_by(&outer));
4584 assert!(outer.is_implied_by(&inner));
4585 }
4586
4587 #[test]
4588 fn test_relation_is_implied_by_greater_than_equal() {
4589 let inner = Relation::new(
4591 "pkg",
4592 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4593 );
4594 let outer = Relation::new(
4595 "pkg",
4596 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
4597 );
4598 assert!(inner.is_implied_by(&outer));
4599 assert!(!outer.is_implied_by(&inner));
4600
4601 let outer = Relation::new(
4603 "pkg",
4604 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4605 );
4606 assert!(inner.is_implied_by(&outer));
4607
4608 let outer = Relation::new(
4610 "pkg",
4611 Some((VersionConstraint::GreaterThan, "1.5".parse().unwrap())),
4612 );
4613 assert!(inner.is_implied_by(&outer));
4614
4615 let inner = Relation::new(
4617 "pkg",
4618 Some((VersionConstraint::GreaterThanEqual, "3.0".parse().unwrap())),
4619 );
4620 let outer = Relation::new(
4621 "pkg",
4622 Some((VersionConstraint::GreaterThan, "3.0".parse().unwrap())),
4623 );
4624 assert!(!inner.is_implied_by(&outer));
4625 }
4626
4627 #[test]
4628 fn test_relation_is_implied_by_less_than_equal() {
4629 let inner = Relation::new(
4631 "pkg",
4632 Some((VersionConstraint::LessThanEqual, "2.0".parse().unwrap())),
4633 );
4634 let outer = Relation::new(
4635 "pkg",
4636 Some((VersionConstraint::LessThanEqual, "1.0".parse().unwrap())),
4637 );
4638 assert!(inner.is_implied_by(&outer));
4639 assert!(!outer.is_implied_by(&inner));
4640
4641 let outer = Relation::new(
4643 "pkg",
4644 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4645 );
4646 assert!(inner.is_implied_by(&outer));
4647
4648 let outer = Relation::new(
4650 "pkg",
4651 Some((VersionConstraint::LessThan, "1.5".parse().unwrap())),
4652 );
4653 assert!(inner.is_implied_by(&outer));
4654 }
4655
4656 #[test]
4657 fn test_relation_is_implied_by_equal() {
4658 let inner = Relation::new(
4660 "pkg",
4661 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4662 );
4663 let outer = Relation::new(
4664 "pkg",
4665 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4666 );
4667 assert!(inner.is_implied_by(&outer));
4668
4669 let outer = Relation::new(
4671 "pkg",
4672 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4673 );
4674 assert!(!inner.is_implied_by(&outer));
4675
4676 let outer = Relation::new(
4678 "pkg",
4679 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4680 );
4681 assert!(!inner.is_implied_by(&outer));
4682 }
4683
4684 #[test]
4685 fn test_relation_is_implied_by_greater_than() {
4686 let inner = Relation::new(
4688 "pkg",
4689 Some((VersionConstraint::GreaterThan, "1.0".parse().unwrap())),
4690 );
4691 let outer = Relation::new(
4692 "pkg",
4693 Some((VersionConstraint::GreaterThan, "2.0".parse().unwrap())),
4694 );
4695 assert!(inner.is_implied_by(&outer));
4696
4697 let outer = Relation::new(
4699 "pkg",
4700 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
4701 );
4702 assert!(inner.is_implied_by(&outer));
4703
4704 let outer = Relation::new(
4706 "pkg",
4707 Some((VersionConstraint::GreaterThanEqual, "1.5".parse().unwrap())),
4708 );
4709 assert!(inner.is_implied_by(&outer));
4710
4711 let outer = Relation::new(
4713 "pkg",
4714 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4715 );
4716 assert!(!inner.is_implied_by(&outer));
4717 }
4718
4719 #[test]
4720 fn test_relation_is_implied_by_less_than() {
4721 let inner = Relation::new(
4723 "pkg",
4724 Some((VersionConstraint::LessThan, "2.0".parse().unwrap())),
4725 );
4726 let outer = Relation::new(
4727 "pkg",
4728 Some((VersionConstraint::LessThan, "1.0".parse().unwrap())),
4729 );
4730 assert!(inner.is_implied_by(&outer));
4731
4732 let outer = Relation::new(
4734 "pkg",
4735 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
4736 );
4737 assert!(inner.is_implied_by(&outer));
4738
4739 let outer = Relation::new(
4741 "pkg",
4742 Some((VersionConstraint::LessThanEqual, "1.5".parse().unwrap())),
4743 );
4744 assert!(inner.is_implied_by(&outer));
4745 }
4746
4747 #[test]
4748 fn test_relation_is_implied_by_incompatible_constraints() {
4749 let inner = Relation::new(
4751 "pkg",
4752 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap())),
4753 );
4754 let outer = Relation::new(
4755 "pkg",
4756 Some((VersionConstraint::LessThanEqual, "2.0".parse().unwrap())),
4757 );
4758 assert!(!inner.is_implied_by(&outer));
4759 assert!(!outer.is_implied_by(&inner));
4760 }
4761
4762 #[test]
4763 fn test_entry_is_implied_by_identical() {
4764 let inner: Entry = "pkg (>= 1.0)".parse().unwrap();
4765 let outer: Entry = "pkg (>= 1.0)".parse().unwrap();
4766 assert!(inner.is_implied_by(&outer));
4767 }
4768
4769 #[test]
4770 fn test_entry_is_implied_by_or_group() {
4771 let inner: Entry = "pkg (>= 1.0)".parse().unwrap();
4773 let outer: Entry = "pkg (>= 1.5) | libc6".parse().unwrap();
4774 assert!(inner.is_implied_by(&outer));
4775 }
4776
4777 #[test]
4778 fn test_entry_is_implied_by_simple_or() {
4779 let inner: Entry = "pkg1 | pkg2".parse().unwrap();
4781 let outer: Entry = "pkg1".parse().unwrap();
4782 assert!(inner.is_implied_by(&outer));
4783
4784 let outer: Entry = "pkg2".parse().unwrap();
4786 assert!(inner.is_implied_by(&outer));
4787 }
4788
4789 #[test]
4790 fn test_entry_is_implied_by_not_implied() {
4791 let inner: Entry = "pkg (>= 2.0)".parse().unwrap();
4793 let outer: Entry = "pkg (>= 1.0)".parse().unwrap();
4794 assert!(!inner.is_implied_by(&outer));
4795 }
4796
4797 #[test]
4798 fn test_entry_is_implied_by_different_packages() {
4799 let inner: Entry = "pkg1".parse().unwrap();
4800 let outer: Entry = "pkg2".parse().unwrap();
4801 assert!(!inner.is_implied_by(&outer));
4802 }
4803
4804 #[test]
4805 fn test_entry_is_implied_by_complex_or() {
4806 let inner: Entry = "pkg1 | pkg2".parse().unwrap();
4808 let outer: Entry = "pkg1 | pkg2".parse().unwrap();
4809 assert!(inner.is_implied_by(&outer));
4810
4811 let outer: Entry = "pkg1 | pkg2 | pkg3".parse().unwrap();
4813 assert!(inner.is_implied_by(&outer));
4814 }
4815
4816 #[test]
4817 fn test_parse_version_with_epoch() {
4818 let input = "amule-dbg (<< 1:2.3.2-2~)";
4821 let parsed: Relations = input.parse().unwrap();
4822 assert_eq!(parsed.to_string(), input);
4823 assert_eq!(parsed.entries().count(), 1);
4824 let entry = parsed.entries().next().unwrap();
4825 assert_eq!(entry.to_string(), "amule-dbg (<< 1:2.3.2-2~)");
4826 assert_eq!(entry.relations().count(), 1);
4827 let relation = entry.relations().next().unwrap();
4828 assert_eq!(relation.name(), "amule-dbg");
4829 assert_eq!(relation.to_string(), "amule-dbg (<< 1:2.3.2-2~)");
4830 assert_eq!(
4831 relation.version(),
4832 Some((VersionConstraint::LessThan, "1:2.3.2-2~".parse().unwrap()))
4833 );
4834 }
4835
4836 #[test]
4837 fn test_ensure_relation_add_new() {
4838 let mut relations: Relations = "python3".parse().unwrap();
4840 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4841 let added = relations.ensure_relation(new_entry);
4842 assert!(added);
4843 assert_eq!(relations.to_string(), "debhelper (>= 12), python3");
4845 }
4846
4847 #[test]
4848 fn test_ensure_relation_already_satisfied() {
4849 let mut relations: Relations = "debhelper (>= 13)".parse().unwrap();
4851 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4852 let added = relations.ensure_relation(new_entry);
4853 assert!(!added);
4854 assert_eq!(relations.to_string(), "debhelper (>= 13)");
4855 }
4856
4857 #[test]
4858 fn test_ensure_relation_replace_weaker() {
4859 let mut relations: Relations = "debhelper (>= 11)".parse().unwrap();
4861 let new_entry: Entry = "debhelper (>= 13)".parse().unwrap();
4862 let added = relations.ensure_relation(new_entry);
4863 assert!(added);
4864 assert_eq!(relations.to_string(), "debhelper (>= 13)");
4865 }
4866
4867 #[test]
4868 fn test_ensure_relation_replace_multiple_weaker() {
4869 let mut relations: Relations = "debhelper (>= 11), debhelper (>= 10), python3"
4871 .parse()
4872 .unwrap();
4873 let new_entry: Entry = "debhelper (>= 13)".parse().unwrap();
4874 let added = relations.ensure_relation(new_entry);
4875 assert!(added);
4876 assert_eq!(relations.to_string(), "debhelper (>= 13), python3");
4877 }
4878
4879 #[test]
4880 fn test_ensure_relation_identical_entry() {
4881 let mut relations: Relations = "debhelper (>= 12)".parse().unwrap();
4883 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4884 let added = relations.ensure_relation(new_entry);
4885 assert!(!added);
4886 assert_eq!(relations.to_string(), "debhelper (>= 12)");
4887 }
4888
4889 #[test]
4890 fn test_ensure_relation_no_version_constraint() {
4891 let mut relations: Relations = "python3".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, python3");
4898 }
4899
4900 #[test]
4901 fn test_ensure_relation_strengthen_unversioned() {
4902 let mut relations: Relations = "debhelper".parse().unwrap();
4905 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4906 let added = relations.ensure_relation(new_entry);
4907 assert!(added);
4908 assert_eq!(relations.to_string(), "debhelper (>= 12)");
4909 }
4910
4911 #[test]
4912 fn test_ensure_relation_versioned_implies_unversioned() {
4913 let mut relations: Relations = "debhelper (>= 12)".parse().unwrap();
4916 let new_entry: Entry = "debhelper".parse().unwrap();
4917 let added = relations.ensure_relation(new_entry);
4918 assert!(!added);
4919 assert_eq!(relations.to_string(), "debhelper (>= 12)");
4920 }
4921
4922 #[test]
4923 fn test_ensure_relation_preserves_whitespace() {
4924 let mut relations: Relations = "python3, rustc".parse().unwrap();
4926 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4927 let added = relations.ensure_relation(new_entry);
4928 assert!(added);
4929 assert_eq!(relations.to_string(), "debhelper (>= 12), python3, rustc");
4931 }
4932
4933 #[test]
4934 fn test_ensure_relation_empty_relations() {
4935 let mut relations: Relations = Relations::new();
4937 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4938 let added = relations.ensure_relation(new_entry);
4939 assert!(added);
4940 assert_eq!(relations.to_string(), "debhelper (>= 12)");
4941 }
4942
4943 #[test]
4944 fn test_ensure_relation_alternative_dependencies() {
4945 let mut relations: Relations = "python3 | python3-minimal".parse().unwrap();
4947 let new_entry: Entry = "debhelper (>= 12)".parse().unwrap();
4948 let added = relations.ensure_relation(new_entry);
4949 assert!(added);
4950 assert_eq!(
4952 relations.to_string(),
4953 "debhelper (>= 12), python3 | python3-minimal"
4954 );
4955 }
4956
4957 #[test]
4958 fn test_ensure_relation_replace_in_middle() {
4959 let mut relations: Relations = "python3, debhelper (>= 11), rustc".parse().unwrap();
4961 let new_entry: Entry = "debhelper (>= 13)".parse().unwrap();
4962 let added = relations.ensure_relation(new_entry);
4963 assert!(added);
4964 assert_eq!(relations.to_string(), "python3, debhelper (>= 13), rustc");
4965 }
4966
4967 #[test]
4968 fn test_ensure_relation_with_different_package() {
4969 let mut relations: Relations = "python3, debhelper (>= 12)".parse().unwrap();
4971 let new_entry: Entry = "rustc".parse().unwrap();
4972 let added = relations.ensure_relation(new_entry);
4973 assert!(added);
4974 assert_eq!(relations.to_string(), "python3, debhelper (>= 12), rustc");
4975 }
4976
4977 #[test]
4978 fn test_parse_invalid_token_in_arch_list() {
4979 let input = "foo [>= bar]";
4980 let result: Result<Relations, _> = input.parse();
4981 assert!(
4982 result.is_err(),
4983 "Expected error for invalid token in architecture list"
4984 );
4985 }
4986
4987 #[test]
4988 fn test_parse_invalid_token_in_profile_list() {
4989 let input = "foo <[] baz>";
4990 let result: Result<Relations, _> = input.parse();
4991 assert!(
4992 result.is_err(),
4993 "Expected error for invalid token in profile list"
4994 );
4995 }
4996
4997 #[test]
4998 fn test_parse_relaxed_unterminated_arch_list() {
4999 let (relations, errors) = Relations::parse_relaxed("libc6 [", true);
5000 assert!(!errors.is_empty());
5001 assert_eq!(relations.to_string(), "libc6 [");
5002 }
5003
5004 #[test]
5005 fn test_parse_relaxed_partial_arch_name() {
5006 let (relations, errors) = Relations::parse_relaxed("libc6 [amd", true);
5007 assert!(!errors.is_empty());
5008 assert_eq!(relations.to_string(), "libc6 [amd");
5009 }
5010
5011 #[test]
5012 fn test_parse_relaxed_unterminated_profile_list() {
5013 let (relations, errors) = Relations::parse_relaxed("libc6 <cross", true);
5014 assert!(!errors.is_empty());
5015 assert_eq!(relations.to_string(), "libc6 <cross");
5016 }
5017
5018 #[test]
5019 fn test_parse_relaxed_unterminated_substvar() {
5020 let (relations, errors) = Relations::parse_relaxed("${shlibs:Depends", true);
5021 assert!(!errors.is_empty());
5022 assert_eq!(relations.to_string(), "${shlibs:Depends");
5023 }
5024
5025 #[test]
5026 fn test_parse_relaxed_empty_substvar() {
5027 let (relations, errors) = Relations::parse_relaxed("${", true);
5028 assert!(!errors.is_empty());
5029 assert_eq!(relations.to_string(), "${");
5030 }
5031
5032 #[test]
5033 fn test_parse_with_comments() {
5034 let input = "dh-python,\nlibsvn-dev,\n# python-all-dbg (>= 2.6.6-3),\npython3-all-dev,\n# python3-all-dbg,\npython3-docutils";
5035 let relations: Relations = input.parse().unwrap();
5036 let entries: Vec<_> = relations.entries().collect();
5037 assert_eq!(entries.len(), 4);
5038 assert_eq!(entries[0].to_string(), "dh-python");
5039 assert_eq!(entries[1].to_string(), "libsvn-dev");
5040 assert_eq!(entries[2].to_string(), "python3-all-dev");
5041 assert_eq!(entries[3].to_string(), "python3-docutils");
5042 assert_eq!(relations.to_string(), input);
5044 }
5045
5046 #[test]
5047 fn test_remove_entry_with_adjacent_comment() {
5048 let input = "dh-python,\n# commented-out,\npython3-all-dev";
5049 let mut relations: Relations = input.parse().unwrap();
5050 assert_eq!(relations.entries().count(), 2);
5051 relations.remove_entry(0);
5052 assert_eq!(relations.entries().count(), 1);
5053 assert_eq!(
5054 relations.entries().next().unwrap().to_string(),
5055 "python3-all-dev"
5056 );
5057 }
5058
5059 #[test]
5060 fn test_insert_entry_with_comments_present() {
5061 let input = "dh-python,\n# commented-out,\npython3-all-dev";
5062 let mut relations: Relations = input.parse().unwrap();
5063 let new_entry: Entry = "libfoo-dev".parse().unwrap();
5064 relations.push(new_entry);
5065 let entries: Vec<_> = relations.entries().collect();
5067 assert_eq!(entries.len(), 3);
5068 assert_eq!(entries[2].to_string(), "libfoo-dev");
5069 }
5070
5071 #[test]
5072 fn test_drop_dependency_with_comments() {
5073 let input = "dh-python,\n# commented-out,\npython3-all-dev,\nlibfoo-dev";
5074 let mut relations: Relations = input.parse().unwrap();
5075 assert!(relations.drop_dependency("python3-all-dev"));
5076 let entries: Vec<_> = relations.entries().collect();
5077 assert_eq!(entries.len(), 2);
5078 assert_eq!(entries[0].to_string(), "dh-python");
5079 assert_eq!(entries[1].to_string(), "libfoo-dev");
5080 }
5081
5082 #[test]
5083 fn test_relation_remove_first_with_malformed_tree() {
5084 let (relations, errors) = Relations::parse_relaxed("foo @ bar | baz", false);
5087 assert!(!errors.is_empty());
5088 let entry = relations.get_entry(0).unwrap();
5089 let mut relation = entry.get_relation(0).unwrap();
5090 relation.remove();
5092 }
5093}