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)]
46enum Lang {}
47impl rowan::Language for Lang {
48 type Kind = SyntaxKind;
49 fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
50 unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
51 }
52 fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
53 kind.into()
54 }
55}
56
57use rowan::{GreenNode, GreenToken};
60
61use rowan::GreenNodeBuilder;
65
66struct Parse {
69 green_node: GreenNode,
70 #[allow(unused)]
71 errors: Vec<String>,
72}
73
74fn parse(text: &str, allow_substvar: bool) -> Parse {
75 struct Parser {
76 tokens: Vec<(SyntaxKind, String)>,
79 builder: GreenNodeBuilder<'static>,
81 errors: Vec<String>,
84 allow_substvar: bool,
86 }
87
88 impl Parser {
89 fn parse_substvar(&mut self) {
90 self.builder.start_node(SyntaxKind::SUBSTVAR.into());
91 self.bump();
92 if self.current() != Some(L_CURLY) {
93 self.error(format!("expected {{ but got {:?}", self.current()).to_string());
94 } else {
95 self.bump();
96 }
97 loop {
98 match self.current() {
99 Some(IDENT) | Some(COLON) => {
100 self.bump();
101 }
102 Some(R_CURLY) => {
103 break;
104 }
105 e => {
106 self.error(format!("expected identifier or : but got {:?}", e).to_string());
107 }
108 }
109 }
110 if self.current() != Some(R_CURLY) {
111 self.error(format!("expected }} but got {:?}", self.current()).to_string());
112 } else {
113 self.bump();
114 }
115 self.builder.finish_node();
116 }
117
118 fn parse_entry(&mut self) {
119 self.skip_ws();
120 self.builder.start_node(SyntaxKind::ENTRY.into());
121 loop {
122 self.parse_relation();
123 match self.peek_past_ws() {
124 Some(COMMA) => {
125 break;
126 }
127 Some(PIPE) => {
128 self.skip_ws();
129 self.bump();
130 self.skip_ws();
131 }
132 None => {
133 self.skip_ws();
134 break;
135 }
136 _ => {
137 self.skip_ws();
138 self.builder.start_node(SyntaxKind::ERROR.into());
139 match self.tokens.pop() {
140 Some((k, t)) => {
141 self.builder.token(k.into(), t.as_str());
142 self.errors
143 .push(format!("Expected comma or pipe, not {:?}", (k, t)));
144 }
145 None => {
146 self.errors
147 .push("Expected comma or pipe, got end of file".to_string());
148 }
149 }
150 self.builder.finish_node();
151 }
152 }
153 }
154 self.builder.finish_node();
155 }
156
157 fn error(&mut self, error: String) {
158 self.errors.push(error);
159 self.builder.start_node(SyntaxKind::ERROR.into());
160 if self.current().is_some() {
161 self.bump();
162 }
163 self.builder.finish_node();
164 }
165
166 fn parse_relation(&mut self) {
167 self.builder.start_node(SyntaxKind::RELATION.into());
168 if self.current() == Some(IDENT) {
169 self.bump();
170 } else {
171 self.error("Expected package name".to_string());
172 }
173 match self.peek_past_ws() {
174 Some(COLON) => {
175 self.skip_ws();
176 self.builder.start_node(ARCHQUAL.into());
177 self.bump();
178 self.skip_ws();
179 if self.current() == Some(IDENT) {
180 self.bump();
181 } else {
182 self.error("Expected architecture name".to_string());
183 }
184 self.builder.finish_node();
185 self.skip_ws();
186 }
187 Some(PIPE) | Some(COMMA) => {}
188 None | Some(L_PARENS) | Some(L_BRACKET) | Some(L_ANGLE) => {
189 self.skip_ws();
190 }
191 e => {
192 self.skip_ws();
193 self.error(format!(
194 "Expected ':' or '|' or '[' or '<' or ',' but got {:?}",
195 e
196 ));
197 }
198 }
199
200 if self.peek_past_ws() == Some(L_PARENS) {
201 self.skip_ws();
202 self.builder.start_node(VERSION.into());
203 self.bump();
204 self.skip_ws();
205
206 self.builder.start_node(CONSTRAINT.into());
207
208 while self.current() == Some(L_ANGLE)
209 || self.current() == Some(R_ANGLE)
210 || self.current() == Some(EQUAL)
211 {
212 self.bump();
213 }
214
215 self.builder.finish_node();
216
217 self.skip_ws();
218
219 if self.current() == Some(IDENT) {
220 self.bump();
221 } else {
222 self.error("Expected version".to_string());
223 }
224
225 if self.current() == Some(R_PARENS) {
226 self.bump();
227 } else {
228 self.error("Expected ')'".to_string());
229 }
230
231 self.builder.finish_node();
232 }
233
234 if self.peek_past_ws() == Some(L_BRACKET) {
235 self.skip_ws();
236 self.builder.start_node(ARCHITECTURES.into());
237 self.bump();
238 loop {
239 self.skip_ws();
240 match self.current() {
241 Some(NOT) => {
242 self.bump();
243 }
244 Some(IDENT) => {
245 self.bump();
246 }
247 Some(R_BRACKET) => {
248 self.bump();
249 break;
250 }
251 _ => {
252 self.error("Expected architecture name or '!' or ']'".to_string());
253 }
254 }
255 }
256 self.builder.finish_node();
257 }
258
259 while self.peek_past_ws() == Some(L_ANGLE) {
260 self.skip_ws();
261 self.builder.start_node(PROFILES.into());
262 self.bump();
263
264 loop {
265 self.skip_ws();
266 match self.current() {
267 Some(IDENT) => {
268 self.bump();
269 }
270 Some(NOT) => {
271 self.bump();
272 self.skip_ws();
273 if self.current() == Some(IDENT) {
274 self.bump();
275 } else {
276 self.error("Expected profile".to_string());
277 }
278 }
279 Some(R_ANGLE) => {
280 self.bump();
281 break;
282 }
283 None => {
284 self.error("Expected profile or '>'".to_string());
285 break;
286 }
287 _ => {
288 self.error("Expected profile or '!' or '>'".to_string());
289 }
290 }
291 }
292
293 self.builder.finish_node();
294 }
295
296 self.builder.finish_node();
297 }
298
299 fn parse(mut self) -> Parse {
300 self.builder.start_node(SyntaxKind::ROOT.into());
301
302 self.skip_ws();
303
304 while self.current().is_some() {
305 match self.current() {
306 Some(IDENT) => self.parse_entry(),
307 Some(DOLLAR) => {
308 if self.allow_substvar {
309 self.parse_substvar()
310 } else {
311 self.error("Substvars are not allowed".to_string());
312 }
313 }
314 Some(COMMA) => {
315 }
317 Some(c) => {
318 self.error(format!("expected $ or identifier but got {:?}", c));
319 }
320 None => {
321 self.error("expected identifier but got end of file".to_string());
322 }
323 }
324
325 self.skip_ws();
326 match self.current() {
327 Some(COMMA) => {
328 self.bump();
329 }
330 None => {
331 break;
332 }
333 c => {
334 self.error(format!("expected comma or end of file but got {:?}", c));
335 }
336 }
337 self.skip_ws();
338 }
339
340 self.builder.finish_node();
341 Parse {
343 green_node: self.builder.finish(),
344 errors: self.errors,
345 }
346 }
347 fn bump(&mut self) {
349 let (kind, text) = self.tokens.pop().unwrap();
350 self.builder.token(kind.into(), text.as_str());
351 }
352 fn current(&self) -> Option<SyntaxKind> {
354 self.tokens.last().map(|(kind, _)| *kind)
355 }
356 fn skip_ws(&mut self) {
357 while self.current() == Some(WHITESPACE) || self.current() == Some(NEWLINE) {
358 self.bump()
359 }
360 }
361
362 fn peek_past_ws(&self) -> Option<SyntaxKind> {
363 let mut i = self.tokens.len();
364 while i > 0 {
365 i -= 1;
366 match self.tokens[i].0 {
367 WHITESPACE | NEWLINE => {}
368 _ => return Some(self.tokens[i].0),
369 }
370 }
371 None
372 }
373 }
374
375 let mut tokens = crate::relations::lex(text);
376 tokens.reverse();
377 Parser {
378 tokens,
379 builder: GreenNodeBuilder::new(),
380 errors: Vec::new(),
381 allow_substvar,
382 }
383 .parse()
384}
385
386type SyntaxNode = rowan::SyntaxNode<Lang>;
393#[allow(unused)]
394type SyntaxToken = rowan::SyntaxToken<Lang>;
395#[allow(unused)]
396type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;
397
398impl Parse {
399 fn root_mut(&self) -> Relations {
400 Relations::cast(SyntaxNode::new_root_mut(self.green_node.clone())).unwrap()
401 }
402}
403
404macro_rules! ast_node {
405 ($ast:ident, $kind:ident) => {
406 #[repr(transparent)]
408 pub struct $ast(SyntaxNode);
409 impl $ast {
410 #[allow(unused)]
411 fn cast(node: SyntaxNode) -> Option<Self> {
412 if node.kind() == $kind {
413 Some(Self(node))
414 } else {
415 None
416 }
417 }
418 }
419
420 impl std::fmt::Display for $ast {
421 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
422 f.write_str(&self.0.text().to_string())
423 }
424 }
425 };
426}
427
428ast_node!(Relations, ROOT);
429ast_node!(Entry, ENTRY);
430ast_node!(Relation, RELATION);
431ast_node!(Substvar, SUBSTVAR);
432
433impl PartialEq for Relations {
434 fn eq(&self, other: &Self) -> bool {
435 self.entries().collect::<Vec<_>>() == other.entries().collect::<Vec<_>>()
436 }
437}
438
439impl PartialEq for Entry {
440 fn eq(&self, other: &Self) -> bool {
441 self.relations().collect::<Vec<_>>() == other.relations().collect::<Vec<_>>()
442 }
443}
444
445impl PartialEq for Relation {
446 fn eq(&self, other: &Self) -> bool {
447 self.name() == other.name()
448 && self.version() == other.version()
449 && self.archqual() == other.archqual()
450 && self.architectures().map(|x| x.collect::<HashSet<_>>())
451 == other.architectures().map(|x| x.collect::<HashSet<_>>())
452 && self.profiles().eq(other.profiles())
453 }
454}
455
456#[cfg(feature = "serde")]
457impl serde::Serialize for Relations {
458 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
459 let rep = self.to_string();
460 serializer.serialize_str(&rep)
461 }
462}
463
464#[cfg(feature = "serde")]
465impl<'de> serde::Deserialize<'de> for Relations {
466 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
467 let s = String::deserialize(deserializer)?;
468 let relations = s.parse().map_err(serde::de::Error::custom)?;
469 Ok(relations)
470 }
471}
472
473impl std::fmt::Debug for Relations {
474 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
475 let mut s = f.debug_struct("Relations");
476
477 for entry in self.entries() {
478 s.field("entry", &entry);
479 }
480
481 s.finish()
482 }
483}
484
485impl std::fmt::Debug for Entry {
486 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
487 let mut s = f.debug_struct("Entry");
488
489 for relation in self.relations() {
490 s.field("relation", &relation);
491 }
492
493 s.finish()
494 }
495}
496
497#[cfg(feature = "serde")]
498impl serde::Serialize for Entry {
499 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
500 let rep = self.to_string();
501 serializer.serialize_str(&rep)
502 }
503}
504
505#[cfg(feature = "serde")]
506impl<'de> serde::Deserialize<'de> for Entry {
507 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
508 let s = String::deserialize(deserializer)?;
509 let entry = s.parse().map_err(serde::de::Error::custom)?;
510 Ok(entry)
511 }
512}
513
514impl std::fmt::Debug for Relation {
515 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
516 let mut s = f.debug_struct("Relation");
517
518 s.field("name", &self.name());
519
520 if let Some((vc, version)) = self.version() {
521 s.field("version", &vc);
522 s.field("version", &version);
523 }
524
525 s.finish()
526 }
527}
528
529#[cfg(feature = "serde")]
530impl serde::Serialize for Relation {
531 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
532 let rep = self.to_string();
533 serializer.serialize_str(&rep)
534 }
535}
536
537#[cfg(feature = "serde")]
538impl<'de> serde::Deserialize<'de> for Relation {
539 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
540 let s = String::deserialize(deserializer)?;
541 let relation = s.parse().map_err(serde::de::Error::custom)?;
542 Ok(relation)
543 }
544}
545
546impl Default for Relations {
547 fn default() -> Self {
548 Self::new()
549 }
550}
551
552fn is_special_package_name(name: &str) -> bool {
557 if name.starts_with("${") && name.ends_with('}') {
559 return true;
560 }
561 if name.starts_with('@') && name.ends_with('@') {
563 return true;
564 }
565 false
566}
567
568pub trait SortingOrder {
570 fn lt(&self, name1: &str, name2: &str) -> bool;
574
575 fn ignore(&self, name: &str) -> bool;
577}
578
579#[derive(Debug, Clone, Copy, Default)]
581pub struct DefaultSortingOrder;
582
583impl SortingOrder for DefaultSortingOrder {
584 fn lt(&self, name1: &str, name2: &str) -> bool {
585 let special1 = is_special_package_name(name1);
586 let special2 = is_special_package_name(name2);
587
588 if special1 && !special2 {
590 return false;
591 }
592 if !special1 && special2 {
593 return true;
594 }
595 if special1 && special2 {
596 return false;
598 }
599
600 name1 < name2
602 }
603
604 fn ignore(&self, name: &str) -> bool {
605 is_special_package_name(name)
606 }
607}
608
609#[derive(Debug, Clone, Copy, Default)]
619pub struct WrapAndSortOrder;
620
621impl WrapAndSortOrder {
622 const BUILD_SYSTEMS: &'static [&'static str] = &[
624 "cdbs",
625 "debhelper-compat",
626 "debhelper",
627 "debputy",
628 "dpkg-build-api",
629 "dpkg-dev",
630 ];
631
632 fn get_sort_key<'a>(&self, name: &'a str) -> (i32, &'a str) {
633 if Self::BUILD_SYSTEMS.contains(&name) || name.starts_with("dh-") {
635 return (-1, name);
636 }
637
638 if name
640 .chars()
641 .next()
642 .is_some_and(|c| c.is_ascii_lowercase() || c.is_ascii_digit())
643 {
644 return (0, name);
645 }
646
647 (1, name)
649 }
650}
651
652impl SortingOrder for WrapAndSortOrder {
653 fn lt(&self, name1: &str, name2: &str) -> bool {
654 self.get_sort_key(name1) < self.get_sort_key(name2)
655 }
656
657 fn ignore(&self, _name: &str) -> bool {
658 false
660 }
661}
662
663impl Relations {
664 pub fn new() -> Self {
666 Self::from(vec![])
667 }
668
669 #[must_use]
671 pub fn wrap_and_sort(self) -> Self {
672 let mut entries = self
673 .entries()
674 .map(|e| e.wrap_and_sort())
675 .collect::<Vec<_>>();
676 entries.sort();
677 Self::from(entries)
679 }
680
681 pub fn entries(&self) -> impl Iterator<Item = Entry> + '_ {
683 self.0.children().filter_map(Entry::cast)
684 }
685
686 pub fn iter(&self) -> impl Iterator<Item = Entry> + '_ {
688 self.entries()
689 }
690
691 pub fn get_entry(&self, idx: usize) -> Option<Entry> {
693 self.entries().nth(idx)
694 }
695
696 pub fn remove_entry(&mut self, idx: usize) -> Entry {
698 let mut entry = self.get_entry(idx).unwrap();
699 entry.remove();
700 entry
701 }
702
703 fn collect_whitespace(start: Option<NodeOrToken<SyntaxNode, SyntaxToken>>) -> String {
705 let mut pattern = String::new();
706 let mut current = start;
707 while let Some(token) = current {
708 if matches!(token.kind(), WHITESPACE | NEWLINE) {
709 if let NodeOrToken::Token(t) = &token {
710 pattern.push_str(t.text());
711 }
712 current = token.next_sibling_or_token();
713 } else {
714 break;
715 }
716 }
717 pattern
718 }
719
720 fn to_green(node: &NodeOrToken<SyntaxNode, SyntaxToken>) -> NodeOrToken<GreenNode, GreenToken> {
722 match node {
723 NodeOrToken::Node(n) => NodeOrToken::Node(n.green().into()),
724 NodeOrToken::Token(t) => NodeOrToken::Token(t.green().to_owned()),
725 }
726 }
727
728 fn is_whitespace_token(token: &GreenToken) -> bool {
730 token.kind() == rowan::SyntaxKind(WHITESPACE as u16)
731 || token.kind() == rowan::SyntaxKind(NEWLINE as u16)
732 }
733
734 fn strip_trailing_ws_from_children(
736 mut children: Vec<NodeOrToken<GreenNode, GreenToken>>,
737 ) -> Vec<NodeOrToken<GreenNode, GreenToken>> {
738 while let Some(last) = children.last() {
739 if let NodeOrToken::Token(t) = last {
740 if Self::is_whitespace_token(t) {
741 children.pop();
742 } else {
743 break;
744 }
745 } else {
746 break;
747 }
748 }
749 children
750 }
751
752 fn strip_relation_trailing_ws(relation: &SyntaxNode) -> GreenNode {
754 let children: Vec<_> = relation
755 .children_with_tokens()
756 .map(|c| Self::to_green(&c))
757 .collect();
758 let stripped = Self::strip_trailing_ws_from_children(children);
759 GreenNode::new(relation.kind().into(), stripped)
760 }
761
762 fn build_odd_syntax_nodes(
764 before_ws: &str,
765 after_ws: &str,
766 ) -> Vec<NodeOrToken<GreenNode, GreenToken>> {
767 [
768 (!before_ws.is_empty())
769 .then(|| NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), before_ws))),
770 Some(NodeOrToken::Token(GreenToken::new(COMMA.into(), ","))),
771 (!after_ws.is_empty())
772 .then(|| NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), after_ws))),
773 ]
774 .into_iter()
775 .flatten()
776 .collect()
777 }
778
779 fn detect_odd_syntax(&self) -> Option<(String, String)> {
781 for entry_node in self.entries() {
782 let mut node = entry_node.0.next_sibling_or_token()?;
783
784 let mut before = String::new();
786 while matches!(node.kind(), WHITESPACE | NEWLINE) {
787 if let NodeOrToken::Token(t) = &node {
788 before.push_str(t.text());
789 }
790 node = node.next_sibling_or_token()?;
791 }
792
793 if node.kind() == COMMA && !before.is_empty() {
795 let after = Self::collect_whitespace(node.next_sibling_or_token());
796 return Some((before, after));
797 }
798 }
799 None
800 }
801
802 fn detect_whitespace_pattern(&self, default: &str) -> String {
810 use std::collections::HashMap;
811
812 let entries: Vec<_> = self.entries().collect();
813 let num_entries = entries.len();
814
815 if num_entries == 0 {
816 return String::from(""); }
818
819 if num_entries == 1 {
820 if let Some(node) = entries[0].0.next_sibling_or_token() {
822 if node.kind() == COMMA {
823 let pattern = Self::collect_whitespace(node.next_sibling_or_token());
824 if !pattern.is_empty() {
825 return pattern;
826 }
827 }
828 }
829 return default.to_string(); }
831
832 let mut whitespace_counts: HashMap<String, usize> = HashMap::new();
834
835 for (i, entry) in entries.iter().enumerate() {
836 if i == num_entries - 1 {
837 break; }
839
840 if let Some(mut node) = entry.0.next_sibling_or_token() {
842 while matches!(node.kind(), WHITESPACE | NEWLINE) {
844 if let Some(next) = node.next_sibling_or_token() {
845 node = next;
846 } else {
847 break;
848 }
849 }
850
851 if node.kind() == COMMA {
853 let pattern = Self::collect_whitespace(node.next_sibling_or_token());
854 if !pattern.is_empty() {
855 *whitespace_counts.entry(pattern).or_insert(0) += 1;
856 }
857 }
858 }
859 }
860
861 if whitespace_counts.len() == 1 {
863 if let Some((ws, _)) = whitespace_counts.iter().next() {
864 return ws.clone();
865 }
866 }
867
868 if let Some((ws, _)) = whitespace_counts.iter().max_by_key(|(_, count)| *count) {
870 return ws.clone();
871 }
872
873 default.to_string()
875 }
876
877 pub fn insert_with_separator(&mut self, idx: usize, entry: Entry, default_sep: Option<&str>) {
884 let is_empty = !self.0.children_with_tokens().any(|n| n.kind() == COMMA);
885 let whitespace = self.detect_whitespace_pattern(default_sep.unwrap_or(" "));
886
887 self.strip_trailing_whitespace();
889
890 let odd_syntax = self.detect_odd_syntax();
892
893 let (position, new_children) = if let Some(current_entry) = self.entries().nth(idx) {
894 let to_insert = if idx == 0 && is_empty {
895 vec![entry.0.green().into()]
896 } else if let Some((before_ws, after_ws)) = &odd_syntax {
897 let mut nodes = vec![entry.0.green().into()];
898 nodes.extend(Self::build_odd_syntax_nodes(before_ws, after_ws));
899 nodes
900 } else {
901 vec![
902 entry.0.green().into(),
903 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
904 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), whitespace.as_str())),
905 ]
906 };
907
908 (current_entry.0.index(), to_insert)
909 } else {
910 let child_count = self.0.children_with_tokens().count();
911 let to_insert = if idx == 0 {
912 vec![entry.0.green().into()]
913 } else if let Some((before_ws, after_ws)) = &odd_syntax {
914 let mut nodes = Self::build_odd_syntax_nodes(before_ws, after_ws);
915 nodes.push(entry.0.green().into());
916 nodes
917 } else {
918 vec![
919 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
920 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), whitespace.as_str())),
921 entry.0.green().into(),
922 ]
923 };
924
925 (child_count, to_insert)
926 };
927 self.0 = SyntaxNode::new_root_mut(
929 self.0.replace_with(
930 self.0
931 .green()
932 .splice_children(position..position, new_children),
933 ),
934 );
935 }
936
937 pub fn insert(&mut self, idx: usize, entry: Entry) {
939 self.insert_with_separator(idx, entry, None);
940 }
941
942 fn strip_entry_trailing_ws(entry: &SyntaxNode) -> GreenNode {
944 let mut children: Vec<_> = entry
945 .children_with_tokens()
946 .map(|c| Self::to_green(&c))
947 .collect();
948
949 if let Some(NodeOrToken::Node(last)) = children.last() {
951 if last.kind() == rowan::SyntaxKind(RELATION as u16) {
952 let relation_node = entry.children().last().unwrap();
954 children.pop();
955 children.push(NodeOrToken::Node(
956 Self::strip_relation_trailing_ws(&relation_node).into(),
957 ));
958 }
959 }
960
961 let stripped = Self::strip_trailing_ws_from_children(children);
963 GreenNode::new(ENTRY.into(), stripped)
964 }
965
966 fn strip_trailing_whitespace(&mut self) {
967 let mut children: Vec<_> = self
968 .0
969 .children_with_tokens()
970 .map(|c| Self::to_green(&c))
971 .collect();
972
973 if let Some(NodeOrToken::Node(last)) = children.last() {
975 if last.kind() == rowan::SyntaxKind(ENTRY as u16) {
976 let last_entry = self.0.children().last().unwrap();
977 children.pop();
978 children.push(NodeOrToken::Node(
979 Self::strip_entry_trailing_ws(&last_entry).into(),
980 ));
981 }
982 }
983
984 let stripped = Self::strip_trailing_ws_from_children(children);
986
987 let nc = self.0.children_with_tokens().count();
988 self.0 = SyntaxNode::new_root_mut(
989 self.0
990 .replace_with(self.0.green().splice_children(0..nc, stripped)),
991 );
992 }
993
994 pub fn replace(&mut self, idx: usize, entry: Entry) {
996 let current_entry = self.get_entry(idx).unwrap();
997 self.0.splice_children(
998 current_entry.0.index()..current_entry.0.index() + 1,
999 vec![entry.0.into()],
1000 );
1001 }
1002
1003 pub fn push(&mut self, entry: Entry) {
1005 let pos = self.entries().count();
1006 self.insert(pos, entry);
1007 }
1008
1009 pub fn substvars(&self) -> impl Iterator<Item = String> + '_ {
1011 self.0
1012 .children()
1013 .filter_map(Substvar::cast)
1014 .map(|s| s.to_string())
1015 }
1016
1017 pub fn parse_relaxed(s: &str, allow_substvar: bool) -> (Relations, Vec<String>) {
1019 let parse = parse(s, allow_substvar);
1020 (parse.root_mut(), parse.errors)
1021 }
1022
1023 pub fn satisfied_by(&self, package_version: impl crate::VersionLookup + Copy) -> bool {
1025 self.entries().all(|e| e.satisfied_by(package_version))
1026 }
1027
1028 pub fn is_empty(&self) -> bool {
1030 self.entries().count() == 0
1031 }
1032
1033 pub fn len(&self) -> usize {
1035 self.entries().count()
1036 }
1037
1038 pub fn ensure_minimum_version(&mut self, package: &str, minimum_version: &Version) {
1061 let mut found = false;
1062 let mut obsolete_indices = vec![];
1063 let mut update_idx = None;
1064
1065 let entries: Vec<_> = self.entries().collect();
1066 for (idx, entry) in entries.iter().enumerate() {
1067 let relations: Vec<_> = entry.relations().collect();
1068
1069 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1071 if names.len() > 1 && names.contains(&package.to_string()) {
1072 let is_obsolete = relations.iter().any(|r| {
1074 if r.name() != package {
1075 return false;
1076 }
1077 if let Some((vc, ver)) = r.version() {
1078 matches!(vc, VersionConstraint::GreaterThan if &ver < minimum_version)
1079 || matches!(vc, VersionConstraint::GreaterThanEqual if &ver <= minimum_version)
1080 } else {
1081 false
1082 }
1083 });
1084 if is_obsolete {
1085 obsolete_indices.push(idx);
1086 }
1087 continue;
1088 }
1089
1090 if names.len() == 1 && names[0] == package {
1092 found = true;
1093 let relation = relations.into_iter().next().unwrap();
1094
1095 let should_update = if let Some((vc, ver)) = relation.version() {
1097 match vc {
1098 VersionConstraint::GreaterThanEqual | VersionConstraint::GreaterThan => {
1099 &ver < minimum_version
1100 }
1101 _ => false,
1102 }
1103 } else {
1104 true
1105 };
1106
1107 if should_update {
1108 update_idx = Some(idx);
1109 }
1110 break;
1111 }
1112 }
1113
1114 if let Some(idx) = update_idx {
1116 let relation = Relation::new(
1117 package,
1118 Some((VersionConstraint::GreaterThanEqual, minimum_version.clone())),
1119 );
1120 self.replace(idx, Entry::from(relation));
1121 }
1122
1123 for idx in obsolete_indices.into_iter().rev() {
1125 self.remove_entry(idx);
1126 }
1127
1128 if !found {
1130 let relation = Relation::new(
1131 package,
1132 Some((VersionConstraint::GreaterThanEqual, minimum_version.clone())),
1133 );
1134 self.push(Entry::from(relation));
1135 }
1136 }
1137
1138 pub fn ensure_exact_version(&mut self, package: &str, version: &Version) {
1153 let mut found = false;
1154 let mut update_idx = None;
1155
1156 let entries: Vec<_> = self.entries().collect();
1157 for (idx, entry) in entries.iter().enumerate() {
1158 let relations: Vec<_> = entry.relations().collect();
1159 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1160
1161 if names.len() > 1 && names[0] == package {
1162 panic!("Complex rule for {}, aborting", package);
1163 }
1164
1165 if names.len() == 1 && names[0] == package {
1166 found = true;
1167 let relation = relations.into_iter().next().unwrap();
1168
1169 let should_update = if let Some((vc, ver)) = relation.version() {
1170 vc != VersionConstraint::Equal || &ver != version
1171 } else {
1172 true
1173 };
1174
1175 if should_update {
1176 update_idx = Some(idx);
1177 }
1178 break;
1179 }
1180 }
1181
1182 if let Some(idx) = update_idx {
1184 let relation =
1185 Relation::new(package, Some((VersionConstraint::Equal, version.clone())));
1186 self.replace(idx, Entry::from(relation));
1187 }
1188
1189 if !found {
1190 let relation =
1191 Relation::new(package, Some((VersionConstraint::Equal, version.clone())));
1192 self.push(Entry::from(relation));
1193 }
1194 }
1195
1196 pub fn ensure_some_version(&mut self, package: &str) {
1214 for entry in self.entries() {
1215 let relations: Vec<_> = entry.relations().collect();
1216 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1217
1218 if names.len() > 1 && names[0] == package {
1219 panic!("Complex rule for {}, aborting", package);
1220 }
1221
1222 if names.len() == 1 && names[0] == package {
1223 return;
1225 }
1226 }
1227
1228 let relation = Relation::simple(package);
1230 self.push(Entry::from(relation));
1231 }
1232
1233 pub fn filter_entries<F>(&mut self, keep: F)
1249 where
1250 F: Fn(&Entry) -> bool,
1251 {
1252 let indices_to_remove: Vec<_> = self
1253 .entries()
1254 .enumerate()
1255 .filter_map(|(idx, entry)| if keep(&entry) { None } else { Some(idx) })
1256 .collect();
1257
1258 for idx in indices_to_remove.into_iter().rev() {
1260 self.remove_entry(idx);
1261 }
1262 }
1263
1264 pub fn is_sorted(&self, sorting_order: &impl SortingOrder) -> bool {
1280 let mut last_name: Option<String> = None;
1281 for entry in self.entries() {
1282 let mut relations = entry.relations();
1284 let Some(relation) = relations.next() else {
1285 continue;
1286 };
1287
1288 let name = relation.name();
1289
1290 if sorting_order.ignore(&name) {
1292 continue;
1293 }
1294
1295 if let Some(ref last) = last_name {
1297 if sorting_order.lt(&name, last) {
1298 return false;
1299 }
1300 }
1301
1302 last_name = Some(name);
1303 }
1304 true
1305 }
1306
1307 fn find_insert_position(&self, entry: &Entry) -> usize {
1319 let Some(relation) = entry.relations().next() else {
1321 return self.len();
1323 };
1324 let package_name = relation.name();
1325
1326 let count = self.entries().filter(|e| !e.is_empty()).count();
1328
1329 let sorting_order: Box<dyn SortingOrder> = if count < 2 {
1331 Box::new(WrapAndSortOrder)
1332 } else {
1333 if self.is_sorted(&WrapAndSortOrder) {
1336 Box::new(WrapAndSortOrder)
1337 } else if self.is_sorted(&DefaultSortingOrder) {
1338 Box::new(DefaultSortingOrder)
1339 } else {
1340 return self.len();
1342 }
1343 };
1344
1345 if sorting_order.ignore(&package_name) {
1347 return self.len();
1348 }
1349
1350 let mut position = 0;
1352 for (idx, existing_entry) in self.entries().enumerate() {
1353 let mut existing_relations = existing_entry.relations();
1354 let Some(existing_relation) = existing_relations.next() else {
1355 position += 1;
1357 continue;
1358 };
1359
1360 let existing_name = existing_relation.name();
1361
1362 if sorting_order.ignore(&existing_name) {
1364 position += 1;
1365 continue;
1366 }
1367
1368 if sorting_order.lt(&package_name, &existing_name) {
1370 return idx;
1371 }
1372 position += 1;
1373 }
1374
1375 position
1376 }
1377
1378 pub fn drop_dependency(&mut self, package: &str) -> bool {
1396 let indices_to_remove: Vec<_> = self
1397 .entries()
1398 .enumerate()
1399 .filter_map(|(idx, entry)| {
1400 let relations: Vec<_> = entry.relations().collect();
1401 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1402 if names == vec![package] {
1403 Some(idx)
1404 } else {
1405 None
1406 }
1407 })
1408 .collect();
1409
1410 let found = !indices_to_remove.is_empty();
1411
1412 for idx in indices_to_remove.into_iter().rev() {
1414 self.remove_entry(idx);
1415 }
1416
1417 found
1418 }
1419
1420 pub fn add_dependency(&mut self, entry: Entry, position: Option<usize>) {
1441 let pos = position.unwrap_or_else(|| self.find_insert_position(&entry));
1442 self.insert(pos, entry);
1443 }
1444
1445 pub fn get_relation(&self, package: &str) -> Result<(usize, Entry), String> {
1469 for (idx, entry) in self.entries().enumerate() {
1470 let relations: Vec<_> = entry.relations().collect();
1471 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1472
1473 if names.len() > 1 && names.contains(&package.to_string()) {
1474 return Err(format!("Complex rule for {}, aborting", package));
1475 }
1476
1477 if names.len() == 1 && names[0] == package {
1478 return Ok((idx, entry));
1479 }
1480 }
1481 Err(format!("Package {} not found", package))
1482 }
1483
1484 pub fn iter_relations_for(&self, package: &str) -> impl Iterator<Item = (usize, Entry)> + '_ {
1501 let package = package.to_string();
1502 self.entries().enumerate().filter(move |(_, entry)| {
1503 let names: Vec<_> = entry.relations().map(|r| r.name()).collect();
1504 names.contains(&package)
1505 })
1506 }
1507
1508 pub fn has_relation(&self, package: &str) -> bool {
1525 self.entries()
1526 .any(|entry| entry.relations().any(|r| r.name() == package))
1527 }
1528}
1529
1530impl From<Vec<Entry>> for Relations {
1531 fn from(entries: Vec<Entry>) -> Self {
1532 let mut builder = GreenNodeBuilder::new();
1533 builder.start_node(ROOT.into());
1534 for (i, entry) in entries.into_iter().enumerate() {
1535 if i > 0 {
1536 builder.token(COMMA.into(), ",");
1537 builder.token(WHITESPACE.into(), " ");
1538 }
1539 inject(&mut builder, entry.0);
1540 }
1541 builder.finish_node();
1542 Relations(SyntaxNode::new_root_mut(builder.finish()))
1543 }
1544}
1545
1546impl From<Entry> for Relations {
1547 fn from(entry: Entry) -> Self {
1548 Self::from(vec![entry])
1549 }
1550}
1551
1552impl Default for Entry {
1553 fn default() -> Self {
1554 Self::new()
1555 }
1556}
1557
1558impl PartialOrd for Entry {
1559 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1560 let mut rels_a = self.relations();
1561 let mut rels_b = other.relations();
1562 while let (Some(a), Some(b)) = (rels_a.next(), rels_b.next()) {
1563 match a.cmp(&b) {
1564 std::cmp::Ordering::Equal => continue,
1565 x => return Some(x),
1566 }
1567 }
1568
1569 if rels_a.next().is_some() {
1570 return Some(std::cmp::Ordering::Greater);
1571 }
1572
1573 if rels_b.next().is_some() {
1574 return Some(std::cmp::Ordering::Less);
1575 }
1576
1577 Some(std::cmp::Ordering::Equal)
1578 }
1579}
1580
1581impl Eq for Entry {}
1582
1583impl Ord for Entry {
1584 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1585 self.partial_cmp(other).unwrap()
1586 }
1587}
1588
1589impl Entry {
1590 pub fn new() -> Self {
1592 let mut builder = GreenNodeBuilder::new();
1593 builder.start_node(SyntaxKind::ENTRY.into());
1594 builder.finish_node();
1595 Entry(SyntaxNode::new_root_mut(builder.finish()))
1596 }
1597
1598 pub fn replace(&mut self, idx: usize, relation: Relation) {
1600 let current_relation = self.get_relation(idx).unwrap();
1601
1602 let old_root = current_relation.0;
1603 let new_root = relation.0;
1604 let mut prev = new_root.first_child_or_token();
1606 let mut new_head_len = 0;
1607 while let Some(p) = prev {
1609 if p.kind() == WHITESPACE || p.kind() == NEWLINE {
1610 new_head_len += 1;
1611 prev = p.next_sibling_or_token();
1612 } else {
1613 break;
1614 }
1615 }
1616 let mut new_tail_len = 0;
1617 let mut next = new_root.last_child_or_token();
1618 while let Some(n) = next {
1619 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1620 new_tail_len += 1;
1621 next = n.prev_sibling_or_token();
1622 } else {
1623 break;
1624 }
1625 }
1626 let mut prev = old_root.first_child_or_token();
1628 let mut old_head = vec![];
1629 while let Some(p) = prev {
1630 if p.kind() == WHITESPACE || p.kind() == NEWLINE {
1631 old_head.push(p.clone());
1632 prev = p.next_sibling_or_token();
1633 } else {
1634 break;
1635 }
1636 }
1637 let mut old_tail = vec![];
1638 let mut next = old_root.last_child_or_token();
1639 while let Some(n) = next {
1640 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1641 old_tail.push(n.clone());
1642 next = n.prev_sibling_or_token();
1643 } else {
1644 break;
1645 }
1646 }
1647 new_root.splice_children(0..new_head_len, old_head);
1648 let tail_pos = new_root.children_with_tokens().count() - new_tail_len;
1649 new_root.splice_children(
1650 tail_pos - new_tail_len..tail_pos,
1651 old_tail.into_iter().rev(),
1652 );
1653 let index = old_root.index();
1654 self.0
1655 .splice_children(index..index + 1, vec![new_root.into()]);
1656 }
1657
1658 #[must_use]
1660 pub fn wrap_and_sort(&self) -> Self {
1661 let mut relations = self
1662 .relations()
1663 .map(|r| r.wrap_and_sort())
1664 .collect::<Vec<_>>();
1665 relations.sort();
1667 Self::from(relations)
1668 }
1669
1670 pub fn relations(&self) -> impl Iterator<Item = Relation> + '_ {
1672 self.0.children().filter_map(Relation::cast)
1673 }
1674
1675 pub fn iter(&self) -> impl Iterator<Item = Relation> + '_ {
1677 self.relations()
1678 }
1679
1680 pub fn get_relation(&self, idx: usize) -> Option<Relation> {
1682 self.relations().nth(idx)
1683 }
1684
1685 pub fn remove_relation(&self, idx: usize) -> Relation {
1698 let mut relation = self.get_relation(idx).unwrap();
1699 relation.remove();
1700 relation
1701 }
1702
1703 pub fn satisfied_by(&self, package_version: impl crate::VersionLookup + Copy) -> bool {
1719 self.relations().any(|r| {
1720 let actual = package_version.lookup_version(r.name().as_str());
1721 if let Some((vc, version)) = r.version() {
1722 if let Some(actual) = actual {
1723 match vc {
1724 VersionConstraint::GreaterThanEqual => *actual >= version,
1725 VersionConstraint::LessThanEqual => *actual <= version,
1726 VersionConstraint::Equal => *actual == version,
1727 VersionConstraint::GreaterThan => *actual > version,
1728 VersionConstraint::LessThan => *actual < version,
1729 }
1730 } else {
1731 false
1732 }
1733 } else {
1734 actual.is_some()
1735 }
1736 })
1737 }
1738
1739 pub fn remove(&mut self) {
1750 let mut removed_comma = false;
1751 let is_first = !self
1752 .0
1753 .siblings(Direction::Prev)
1754 .skip(1)
1755 .any(|n| n.kind() == ENTRY);
1756 while let Some(n) = self.0.next_sibling_or_token() {
1757 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1758 n.detach();
1759 } else if n.kind() == COMMA {
1760 n.detach();
1761 removed_comma = true;
1762 break;
1763 } else {
1764 panic!("Unexpected node: {:?}", n);
1765 }
1766 }
1767 if !is_first {
1768 while let Some(n) = self.0.prev_sibling_or_token() {
1769 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1770 n.detach();
1771 } else if !removed_comma && n.kind() == COMMA {
1772 n.detach();
1773 break;
1774 } else {
1775 break;
1776 }
1777 }
1778 } else {
1779 while let Some(n) = self.0.next_sibling_or_token() {
1780 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1781 n.detach();
1782 } else {
1783 break;
1784 }
1785 }
1786 }
1787 self.0.detach();
1788 }
1789
1790 pub fn is_empty(&self) -> bool {
1792 self.relations().count() == 0
1793 }
1794
1795 pub fn len(&self) -> usize {
1797 self.relations().count()
1798 }
1799
1800 pub fn push(&mut self, relation: Relation) {
1813 let is_empty = !self
1814 .0
1815 .children_with_tokens()
1816 .any(|n| n.kind() == PIPE || n.kind() == RELATION);
1817
1818 let (position, new_children) = if let Some(current_relation) = self.relations().last() {
1819 let to_insert: Vec<NodeOrToken<GreenNode, GreenToken>> = if is_empty {
1820 vec![relation.0.green().into()]
1821 } else {
1822 vec![
1823 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
1824 NodeOrToken::Token(GreenToken::new(PIPE.into(), "|")),
1825 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
1826 relation.0.green().into(),
1827 ]
1828 };
1829
1830 (current_relation.0.index() + 1, to_insert)
1831 } else {
1832 let child_count = self.0.children_with_tokens().count();
1833 (
1834 child_count,
1835 if is_empty {
1836 vec![relation.0.green().into()]
1837 } else {
1838 vec![
1839 NodeOrToken::Token(GreenToken::new(PIPE.into(), "|")),
1840 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
1841 relation.0.green().into(),
1842 ]
1843 },
1844 )
1845 };
1846
1847 let new_root = SyntaxNode::new_root_mut(
1848 self.0.replace_with(
1849 self.0
1850 .green()
1851 .splice_children(position..position, new_children),
1852 ),
1853 );
1854
1855 if let Some(parent) = self.0.parent() {
1856 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
1857 self.0 = parent
1858 .children_with_tokens()
1859 .nth(self.0.index())
1860 .unwrap()
1861 .clone()
1862 .into_node()
1863 .unwrap();
1864 } else {
1865 self.0 = new_root;
1866 }
1867 }
1868}
1869
1870fn inject(builder: &mut GreenNodeBuilder, node: SyntaxNode) {
1871 builder.start_node(node.kind().into());
1872 for child in node.children_with_tokens() {
1873 match child {
1874 rowan::NodeOrToken::Node(child) => {
1875 inject(builder, child);
1876 }
1877 rowan::NodeOrToken::Token(token) => {
1878 builder.token(token.kind().into(), token.text());
1879 }
1880 }
1881 }
1882 builder.finish_node();
1883}
1884
1885impl From<Vec<Relation>> for Entry {
1886 fn from(relations: Vec<Relation>) -> Self {
1887 let mut builder = GreenNodeBuilder::new();
1888 builder.start_node(SyntaxKind::ENTRY.into());
1889 for (i, relation) in relations.into_iter().enumerate() {
1890 if i > 0 {
1891 builder.token(WHITESPACE.into(), " ");
1892 builder.token(COMMA.into(), "|");
1893 builder.token(WHITESPACE.into(), " ");
1894 }
1895 inject(&mut builder, relation.0);
1896 }
1897 builder.finish_node();
1898 Entry(SyntaxNode::new_root_mut(builder.finish()))
1899 }
1900}
1901
1902impl From<Relation> for Entry {
1903 fn from(relation: Relation) -> Self {
1904 Self::from(vec![relation])
1905 }
1906}
1907
1908impl Relation {
1909 pub fn new(name: &str, version_constraint: Option<(VersionConstraint, Version)>) -> Self {
1923 let mut builder = GreenNodeBuilder::new();
1924 builder.start_node(SyntaxKind::RELATION.into());
1925 builder.token(IDENT.into(), name);
1926 if let Some((vc, version)) = version_constraint {
1927 builder.token(WHITESPACE.into(), " ");
1928 builder.start_node(SyntaxKind::VERSION.into());
1929 builder.token(L_PARENS.into(), "(");
1930 builder.start_node(SyntaxKind::CONSTRAINT.into());
1931 for c in vc.to_string().chars() {
1932 builder.token(
1933 match c {
1934 '>' => R_ANGLE.into(),
1935 '<' => L_ANGLE.into(),
1936 '=' => EQUAL.into(),
1937 _ => unreachable!(),
1938 },
1939 c.to_string().as_str(),
1940 );
1941 }
1942 builder.finish_node();
1943
1944 builder.token(WHITESPACE.into(), " ");
1945
1946 builder.token(IDENT.into(), version.to_string().as_str());
1947
1948 builder.token(R_PARENS.into(), ")");
1949
1950 builder.finish_node();
1951 }
1952
1953 builder.finish_node();
1954 Relation(SyntaxNode::new_root_mut(builder.finish()))
1955 }
1956
1957 #[must_use]
1966 pub fn wrap_and_sort(&self) -> Self {
1967 let mut builder = GreenNodeBuilder::new();
1968 builder.start_node(SyntaxKind::RELATION.into());
1969 builder.token(IDENT.into(), self.name().as_str());
1970 if let Some(archqual) = self.archqual() {
1971 builder.token(COLON.into(), ":");
1972 builder.token(IDENT.into(), archqual.as_str());
1973 }
1974 if let Some((vc, version)) = self.version() {
1975 builder.token(WHITESPACE.into(), " ");
1976 builder.start_node(SyntaxKind::VERSION.into());
1977 builder.token(L_PARENS.into(), "(");
1978 builder.start_node(SyntaxKind::CONSTRAINT.into());
1979 builder.token(
1980 match vc {
1981 VersionConstraint::GreaterThanEqual => R_ANGLE.into(),
1982 VersionConstraint::LessThanEqual => L_ANGLE.into(),
1983 VersionConstraint::Equal => EQUAL.into(),
1984 VersionConstraint::GreaterThan => R_ANGLE.into(),
1985 VersionConstraint::LessThan => L_ANGLE.into(),
1986 },
1987 vc.to_string().as_str(),
1988 );
1989 builder.finish_node();
1990 builder.token(WHITESPACE.into(), " ");
1991 builder.token(IDENT.into(), version.to_string().as_str());
1992 builder.token(R_PARENS.into(), ")");
1993 builder.finish_node();
1994 }
1995 if let Some(architectures) = self.architectures() {
1996 builder.token(WHITESPACE.into(), " ");
1997 builder.start_node(ARCHITECTURES.into());
1998 builder.token(L_BRACKET.into(), "[");
1999 for (i, arch) in architectures.enumerate() {
2000 if i > 0 {
2001 builder.token(WHITESPACE.into(), " ");
2002 }
2003 builder.token(IDENT.into(), arch.as_str());
2004 }
2005 builder.token(R_BRACKET.into(), "]");
2006 builder.finish_node();
2007 }
2008 for profiles in self.profiles() {
2009 builder.token(WHITESPACE.into(), " ");
2010 builder.start_node(PROFILES.into());
2011 builder.token(L_ANGLE.into(), "<");
2012 for (i, profile) in profiles.into_iter().enumerate() {
2013 if i > 0 {
2014 builder.token(WHITESPACE.into(), " ");
2015 }
2016 match profile {
2017 BuildProfile::Disabled(name) => {
2018 builder.token(NOT.into(), "!");
2019 builder.token(IDENT.into(), name.as_str());
2020 }
2021 BuildProfile::Enabled(name) => {
2022 builder.token(IDENT.into(), name.as_str());
2023 }
2024 }
2025 }
2026 builder.token(R_ANGLE.into(), ">");
2027 builder.finish_node();
2028 }
2029 builder.finish_node();
2030 Relation(SyntaxNode::new_root_mut(builder.finish()))
2031 }
2032
2033 pub fn simple(name: &str) -> Self {
2042 Self::new(name, None)
2043 }
2044
2045 pub fn drop_constraint(&mut self) -> bool {
2056 let version_token = self.0.children().find(|n| n.kind() == VERSION);
2057 if let Some(version_token) = version_token {
2058 while let Some(prev) = version_token.prev_sibling_or_token() {
2060 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
2061 prev.detach();
2062 } else {
2063 break;
2064 }
2065 }
2066 version_token.detach();
2067 return true;
2068 }
2069
2070 false
2071 }
2072
2073 pub fn name(&self) -> String {
2082 self.0
2083 .children_with_tokens()
2084 .find_map(|it| match it {
2085 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2086 _ => None,
2087 })
2088 .unwrap()
2089 .text()
2090 .to_string()
2091 }
2092
2093 pub fn archqual(&self) -> Option<String> {
2102 let archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
2103 let node = if let Some(archqual) = archqual {
2104 archqual.children_with_tokens().find_map(|it| match it {
2105 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2106 _ => None,
2107 })
2108 } else {
2109 None
2110 };
2111 node.map(|n| n.text().to_string())
2112 }
2113
2114 pub fn set_archqual(&mut self, archqual: &str) {
2124 let mut builder = GreenNodeBuilder::new();
2125 builder.start_node(ARCHQUAL.into());
2126 builder.token(COLON.into(), ":");
2127 builder.token(IDENT.into(), archqual);
2128 builder.finish_node();
2129
2130 let node_archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
2131 if let Some(node_archqual) = node_archqual {
2132 self.0.splice_children(
2133 node_archqual.index()..node_archqual.index() + 1,
2134 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2135 );
2136 } else {
2137 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
2138 let idx = if let Some(name_node) = name_node {
2139 name_node.index() + 1
2140 } else {
2141 0
2142 };
2143 self.0.splice_children(
2144 idx..idx,
2145 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2146 );
2147 }
2148 }
2149
2150 pub fn version(&self) -> Option<(VersionConstraint, Version)> {
2152 let vc = self.0.children().find(|n| n.kind() == VERSION);
2153 let vc = vc.as_ref()?;
2154 let constraint = vc.children().find(|n| n.kind() == CONSTRAINT);
2155
2156 let version = vc.children_with_tokens().find_map(|it| match it {
2157 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2158 _ => None,
2159 });
2160
2161 if let (Some(constraint), Some(version)) = (constraint, version) {
2162 let vc: VersionConstraint = constraint.to_string().parse().unwrap();
2163 Some((vc, (version.text().to_string()).parse().unwrap()))
2164 } else {
2165 None
2166 }
2167 }
2168
2169 pub fn set_version(&mut self, version_constraint: Option<(VersionConstraint, Version)>) {
2180 let current_version = self.0.children().find(|n| n.kind() == VERSION);
2181 if let Some((vc, version)) = version_constraint {
2182 let mut builder = GreenNodeBuilder::new();
2183 builder.start_node(VERSION.into());
2184 builder.token(L_PARENS.into(), "(");
2185 builder.start_node(CONSTRAINT.into());
2186 match vc {
2187 VersionConstraint::GreaterThanEqual => {
2188 builder.token(R_ANGLE.into(), ">");
2189 builder.token(EQUAL.into(), "=");
2190 }
2191 VersionConstraint::LessThanEqual => {
2192 builder.token(L_ANGLE.into(), "<");
2193 builder.token(EQUAL.into(), "=");
2194 }
2195 VersionConstraint::Equal => {
2196 builder.token(EQUAL.into(), "=");
2197 }
2198 VersionConstraint::GreaterThan => {
2199 builder.token(R_ANGLE.into(), ">");
2200 }
2201 VersionConstraint::LessThan => {
2202 builder.token(L_ANGLE.into(), "<");
2203 }
2204 }
2205 builder.finish_node(); builder.token(WHITESPACE.into(), " ");
2207 builder.token(IDENT.into(), version.to_string().as_str());
2208 builder.token(R_PARENS.into(), ")");
2209 builder.finish_node(); if let Some(current_version) = current_version {
2212 self.0.splice_children(
2213 current_version.index()..current_version.index() + 1,
2214 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2215 );
2216 } else {
2217 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
2218 let idx = if let Some(name_node) = name_node {
2219 name_node.index() + 1
2220 } else {
2221 0
2222 };
2223 let new_children = vec![
2224 GreenToken::new(WHITESPACE.into(), " ").into(),
2225 builder.finish().into(),
2226 ];
2227 let new_root = SyntaxNode::new_root_mut(
2228 self.0.green().splice_children(idx..idx, new_children),
2229 );
2230 if let Some(parent) = self.0.parent() {
2231 parent
2232 .splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2233 self.0 = parent
2234 .children_with_tokens()
2235 .nth(self.0.index())
2236 .unwrap()
2237 .clone()
2238 .into_node()
2239 .unwrap();
2240 } else {
2241 self.0 = new_root;
2242 }
2243 }
2244 } else if let Some(current_version) = current_version {
2245 while let Some(prev) = current_version.prev_sibling_or_token() {
2247 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
2248 prev.detach();
2249 } else {
2250 break;
2251 }
2252 }
2253 current_version.detach();
2254 }
2255 }
2256
2257 pub fn architectures(&self) -> Option<impl Iterator<Item = String> + '_> {
2266 let architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES)?;
2267
2268 Some(architectures.children_with_tokens().filter_map(|node| {
2269 let token = node.as_token()?;
2270 if token.kind() == IDENT {
2271 Some(token.text().to_string())
2272 } else {
2273 None
2274 }
2275 }))
2276 }
2277
2278 pub fn profiles(&self) -> impl Iterator<Item = Vec<BuildProfile>> + '_ {
2288 let profiles = self.0.children().filter(|n| n.kind() == PROFILES);
2289
2290 profiles.map(|profile| {
2291 let mut ret = vec![];
2293 let mut current = vec![];
2294 for token in profile.children_with_tokens() {
2295 match token.kind() {
2296 WHITESPACE | NEWLINE => {
2297 if !current.is_empty() {
2298 ret.push(current.join("").parse::<BuildProfile>().unwrap());
2299 current = vec![];
2300 }
2301 }
2302 L_ANGLE | R_ANGLE => {}
2303 _ => {
2304 current.push(token.to_string());
2305 }
2306 }
2307 }
2308 if !current.is_empty() {
2309 ret.push(current.concat().parse().unwrap());
2310 }
2311 ret
2312 })
2313 }
2314
2315 pub fn remove(&mut self) {
2326 let is_first = !self
2327 .0
2328 .siblings(Direction::Prev)
2329 .skip(1)
2330 .any(|n| n.kind() == RELATION);
2331 if !is_first {
2332 while let Some(n) = self.0.prev_sibling_or_token() {
2335 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2336 n.detach();
2337 } else if n.kind() == PIPE {
2338 n.detach();
2339 break;
2340 } else {
2341 break;
2342 }
2343 }
2344 while let Some(n) = self.0.prev_sibling_or_token() {
2345 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2346 n.detach();
2347 } else {
2348 break;
2349 }
2350 }
2351 } else {
2352 while let Some(n) = self.0.next_sibling_or_token() {
2355 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2356 n.detach();
2357 } else if n.kind() == PIPE {
2358 n.detach();
2359 break;
2360 } else {
2361 panic!("Unexpected node: {:?}", n);
2362 }
2363 }
2364
2365 while let Some(n) = self.0.next_sibling_or_token() {
2366 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2367 n.detach();
2368 } else {
2369 break;
2370 }
2371 }
2372 }
2373 if let Some(mut parent) = self.0.parent().and_then(Entry::cast) {
2375 if parent.is_empty() {
2376 parent.remove();
2377 } else {
2378 self.0.detach();
2379 }
2380 } else {
2381 self.0.detach();
2382 }
2383 }
2384
2385 pub fn set_architectures<'a>(&mut self, architectures: impl Iterator<Item = &'a str>) {
2395 let mut builder = GreenNodeBuilder::new();
2396 builder.start_node(ARCHITECTURES.into());
2397 builder.token(L_BRACKET.into(), "[");
2398 for (i, arch) in architectures.enumerate() {
2399 if i > 0 {
2400 builder.token(WHITESPACE.into(), " ");
2401 }
2402 builder.token(IDENT.into(), arch);
2403 }
2404 builder.token(R_BRACKET.into(), "]");
2405 builder.finish_node();
2406
2407 let node_architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES);
2408 if let Some(node_architectures) = node_architectures {
2409 let new_root = SyntaxNode::new_root_mut(builder.finish());
2410 self.0.splice_children(
2411 node_architectures.index()..node_architectures.index() + 1,
2412 vec![new_root.into()],
2413 );
2414 } else {
2415 let profiles = self.0.children().find(|n| n.kind() == PROFILES);
2416 let idx = if let Some(profiles) = profiles {
2417 profiles.index()
2418 } else {
2419 self.0.children_with_tokens().count()
2420 };
2421 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
2422 idx..idx,
2423 vec![
2424 GreenToken::new(WHITESPACE.into(), " ").into(),
2425 builder.finish().into(),
2426 ],
2427 ));
2428 if let Some(parent) = self.0.parent() {
2429 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2430 self.0 = parent
2431 .children_with_tokens()
2432 .nth(self.0.index())
2433 .unwrap()
2434 .clone()
2435 .into_node()
2436 .unwrap();
2437 } else {
2438 self.0 = new_root;
2439 }
2440 }
2441 }
2442
2443 pub fn add_profile(&mut self, profile: &[BuildProfile]) {
2454 let mut builder = GreenNodeBuilder::new();
2455 builder.start_node(PROFILES.into());
2456 builder.token(L_ANGLE.into(), "<");
2457 for (i, profile) in profile.iter().enumerate() {
2458 if i > 0 {
2459 builder.token(WHITESPACE.into(), " ");
2460 }
2461 match profile {
2462 BuildProfile::Disabled(name) => {
2463 builder.token(NOT.into(), "!");
2464 builder.token(IDENT.into(), name.as_str());
2465 }
2466 BuildProfile::Enabled(name) => {
2467 builder.token(IDENT.into(), name.as_str());
2468 }
2469 }
2470 }
2471 builder.token(R_ANGLE.into(), ">");
2472 builder.finish_node();
2473
2474 let node_profiles = self.0.children().find(|n| n.kind() == PROFILES);
2475 if let Some(node_profiles) = node_profiles {
2476 let new_root = SyntaxNode::new_root_mut(builder.finish());
2477 self.0.splice_children(
2478 node_profiles.index()..node_profiles.index() + 1,
2479 vec![new_root.into()],
2480 );
2481 } else {
2482 let idx = self.0.children_with_tokens().count();
2483 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
2484 idx..idx,
2485 vec![
2486 GreenToken::new(WHITESPACE.into(), " ").into(),
2487 builder.finish().into(),
2488 ],
2489 ));
2490 if let Some(parent) = self.0.parent() {
2491 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2492 self.0 = parent
2493 .children_with_tokens()
2494 .nth(self.0.index())
2495 .unwrap()
2496 .clone()
2497 .into_node()
2498 .unwrap();
2499 } else {
2500 self.0 = new_root;
2501 }
2502 }
2503 }
2504
2505 pub fn build(name: &str) -> RelationBuilder {
2507 RelationBuilder::new(name)
2508 }
2509}
2510
2511pub struct RelationBuilder {
2525 name: String,
2526 version_constraint: Option<(VersionConstraint, Version)>,
2527 archqual: Option<String>,
2528 architectures: Vec<String>,
2529 profiles: Vec<Vec<BuildProfile>>,
2530}
2531
2532impl RelationBuilder {
2533 fn new(name: &str) -> Self {
2535 Self {
2536 name: name.to_string(),
2537 version_constraint: None,
2538 archqual: None,
2539 architectures: vec![],
2540 profiles: vec![],
2541 }
2542 }
2543
2544 pub fn version_constraint(mut self, vc: VersionConstraint, version: Version) -> Self {
2546 self.version_constraint = Some((vc, version));
2547 self
2548 }
2549
2550 pub fn archqual(mut self, archqual: &str) -> Self {
2552 self.archqual = Some(archqual.to_string());
2553 self
2554 }
2555
2556 pub fn architectures(mut self, architectures: Vec<String>) -> Self {
2558 self.architectures = architectures;
2559 self
2560 }
2561
2562 pub fn profiles(mut self, profiles: Vec<Vec<BuildProfile>>) -> Self {
2564 self.profiles = profiles;
2565 self
2566 }
2567
2568 pub fn add_profile(mut self, profile: Vec<BuildProfile>) -> Self {
2570 self.profiles.push(profile);
2571 self
2572 }
2573
2574 pub fn build(self) -> Relation {
2576 let mut relation = Relation::new(&self.name, self.version_constraint);
2577 if let Some(archqual) = &self.archqual {
2578 relation.set_archqual(archqual);
2579 }
2580 relation.set_architectures(self.architectures.iter().map(|s| s.as_str()));
2581 for profile in &self.profiles {
2582 relation.add_profile(profile);
2583 }
2584 relation
2585 }
2586}
2587
2588impl PartialOrd for Relation {
2589 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
2590 let name_cmp = self.name().cmp(&other.name());
2592 if name_cmp != std::cmp::Ordering::Equal {
2593 return Some(name_cmp);
2594 }
2595
2596 let self_version = self.version();
2597 let other_version = other.version();
2598
2599 match (self_version, other_version) {
2600 (Some((self_vc, self_version)), Some((other_vc, other_version))) => {
2601 let vc_cmp = self_vc.cmp(&other_vc);
2602 if vc_cmp != std::cmp::Ordering::Equal {
2603 return Some(vc_cmp);
2604 }
2605
2606 Some(self_version.cmp(&other_version))
2607 }
2608 (Some(_), None) => Some(std::cmp::Ordering::Greater),
2609 (None, Some(_)) => Some(std::cmp::Ordering::Less),
2610 (None, None) => Some(std::cmp::Ordering::Equal),
2611 }
2612 }
2613}
2614
2615impl Eq for Relation {}
2616
2617impl Ord for Relation {
2618 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
2619 self.partial_cmp(other).unwrap()
2620 }
2621}
2622
2623impl std::str::FromStr for Relations {
2624 type Err = String;
2625
2626 fn from_str(s: &str) -> Result<Self, Self::Err> {
2627 let parse = parse(s, false);
2628 if parse.errors.is_empty() {
2629 Ok(parse.root_mut())
2630 } else {
2631 Err(parse.errors.join("\n"))
2632 }
2633 }
2634}
2635
2636impl std::str::FromStr for Entry {
2637 type Err = String;
2638
2639 fn from_str(s: &str) -> Result<Self, Self::Err> {
2640 let root: Relations = s.parse()?;
2641
2642 let mut entries = root.entries();
2643 let entry = if let Some(entry) = entries.next() {
2644 entry
2645 } else {
2646 return Err("No entry found".to_string());
2647 };
2648
2649 if entries.next().is_some() {
2650 return Err("Multiple entries found".to_string());
2651 }
2652
2653 Ok(entry)
2654 }
2655}
2656
2657impl std::str::FromStr for Relation {
2658 type Err = String;
2659
2660 fn from_str(s: &str) -> Result<Self, Self::Err> {
2661 let entry: Entry = s.parse()?;
2662
2663 let mut relations = entry.relations();
2664 let relation = if let Some(relation) = relations.next() {
2665 relation
2666 } else {
2667 return Err("No relation found".to_string());
2668 };
2669
2670 if relations.next().is_some() {
2671 return Err("Multiple relations found".to_string());
2672 }
2673
2674 Ok(relation)
2675 }
2676}
2677
2678impl From<crate::lossy::Relation> for Relation {
2679 fn from(relation: crate::lossy::Relation) -> Self {
2680 let mut builder = Relation::build(&relation.name);
2681
2682 if let Some((vc, version)) = relation.version {
2683 builder = builder.version_constraint(vc, version);
2684 }
2685
2686 if let Some(archqual) = relation.archqual {
2687 builder = builder.archqual(&archqual);
2688 }
2689
2690 if let Some(architectures) = relation.architectures {
2691 builder = builder.architectures(architectures);
2692 }
2693
2694 builder = builder.profiles(relation.profiles);
2695
2696 builder.build()
2697 }
2698}
2699
2700impl From<Relation> for crate::lossy::Relation {
2701 fn from(relation: Relation) -> Self {
2702 crate::lossy::Relation {
2703 name: relation.name(),
2704 version: relation.version(),
2705 archqual: relation.archqual(),
2706 architectures: relation.architectures().map(|a| a.collect()),
2707 profiles: relation.profiles().collect(),
2708 }
2709 }
2710}
2711
2712impl From<Entry> for Vec<crate::lossy::Relation> {
2713 fn from(entry: Entry) -> Self {
2714 entry.relations().map(|r| r.into()).collect()
2715 }
2716}
2717
2718impl From<Vec<crate::lossy::Relation>> for Entry {
2719 fn from(relations: Vec<crate::lossy::Relation>) -> Self {
2720 let relations: Vec<Relation> = relations.into_iter().map(|r| r.into()).collect();
2721 Entry::from(relations)
2722 }
2723}
2724
2725#[cfg(test)]
2726mod tests {
2727 use super::*;
2728
2729 #[test]
2730 fn test_parse() {
2731 let input = "python3-dulwich";
2732 let parsed: Relations = input.parse().unwrap();
2733 assert_eq!(parsed.to_string(), input);
2734 assert_eq!(parsed.entries().count(), 1);
2735 let entry = parsed.entries().next().unwrap();
2736 assert_eq!(entry.to_string(), "python3-dulwich");
2737 assert_eq!(entry.relations().count(), 1);
2738 let relation = entry.relations().next().unwrap();
2739 assert_eq!(relation.to_string(), "python3-dulwich");
2740 assert_eq!(relation.version(), None);
2741
2742 let input = "python3-dulwich (>= 0.20.21)";
2743 let parsed: Relations = input.parse().unwrap();
2744 assert_eq!(parsed.to_string(), input);
2745 assert_eq!(parsed.entries().count(), 1);
2746 let entry = parsed.entries().next().unwrap();
2747 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
2748 assert_eq!(entry.relations().count(), 1);
2749 let relation = entry.relations().next().unwrap();
2750 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
2751 assert_eq!(
2752 relation.version(),
2753 Some((
2754 VersionConstraint::GreaterThanEqual,
2755 "0.20.21".parse().unwrap()
2756 ))
2757 );
2758 }
2759
2760 #[test]
2761 fn test_multiple() {
2762 let input = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)";
2763 let parsed: Relations = input.parse().unwrap();
2764 assert_eq!(parsed.to_string(), input);
2765 assert_eq!(parsed.entries().count(), 2);
2766 let entry = parsed.entries().next().unwrap();
2767 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
2768 assert_eq!(entry.relations().count(), 1);
2769 let relation = entry.relations().next().unwrap();
2770 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
2771 assert_eq!(
2772 relation.version(),
2773 Some((
2774 VersionConstraint::GreaterThanEqual,
2775 "0.20.21".parse().unwrap()
2776 ))
2777 );
2778 let entry = parsed.entries().nth(1).unwrap();
2779 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.21)");
2780 assert_eq!(entry.relations().count(), 1);
2781 let relation = entry.relations().next().unwrap();
2782 assert_eq!(relation.to_string(), "python3-dulwich (<< 0.21)");
2783 assert_eq!(
2784 relation.version(),
2785 Some((VersionConstraint::LessThan, "0.21".parse().unwrap()))
2786 );
2787 }
2788
2789 #[test]
2790 fn test_architectures() {
2791 let input = "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]";
2792 let parsed: Relations = input.parse().unwrap();
2793 assert_eq!(parsed.to_string(), input);
2794 assert_eq!(parsed.entries().count(), 1);
2795 let entry = parsed.entries().next().unwrap();
2796 assert_eq!(
2797 entry.to_string(),
2798 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
2799 );
2800 assert_eq!(entry.relations().count(), 1);
2801 let relation = entry.relations().next().unwrap();
2802 assert_eq!(
2803 relation.to_string(),
2804 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
2805 );
2806 assert_eq!(relation.version(), None);
2807 assert_eq!(
2808 relation.architectures().unwrap().collect::<Vec<_>>(),
2809 vec![
2810 "amd64", "arm64", "armhf", "i386", "mips", "mips64el", "mipsel", "ppc64el", "s390x"
2811 ]
2812 .into_iter()
2813 .map(|s| s.to_string())
2814 .collect::<Vec<_>>()
2815 );
2816 }
2817
2818 #[test]
2819 fn test_profiles() {
2820 let input = "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>, bar";
2821 let parsed: Relations = input.parse().unwrap();
2822 assert_eq!(parsed.to_string(), input);
2823 assert_eq!(parsed.entries().count(), 2);
2824 let entry = parsed.entries().next().unwrap();
2825 assert_eq!(
2826 entry.to_string(),
2827 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
2828 );
2829 assert_eq!(entry.relations().count(), 1);
2830 let relation = entry.relations().next().unwrap();
2831 assert_eq!(
2832 relation.to_string(),
2833 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
2834 );
2835 assert_eq!(
2836 relation.version(),
2837 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap()))
2838 );
2839 assert_eq!(
2840 relation.architectures().unwrap().collect::<Vec<_>>(),
2841 vec!["i386", "arm"]
2842 .into_iter()
2843 .map(|s| s.to_string())
2844 .collect::<Vec<_>>()
2845 );
2846 assert_eq!(
2847 relation.profiles().collect::<Vec<_>>(),
2848 vec![
2849 vec![BuildProfile::Disabled("nocheck".to_string())],
2850 vec![BuildProfile::Disabled("cross".to_string())]
2851 ]
2852 );
2853 }
2854
2855 #[test]
2856 fn test_substvar() {
2857 let input = "${shlibs:Depends}";
2858
2859 let (parsed, errors) = Relations::parse_relaxed(input, true);
2860 assert_eq!(errors, Vec::<String>::new());
2861 assert_eq!(parsed.to_string(), input);
2862 assert_eq!(parsed.entries().count(), 0);
2863
2864 assert_eq!(
2865 parsed.substvars().collect::<Vec<_>>(),
2866 vec!["${shlibs:Depends}"]
2867 );
2868 }
2869
2870 #[test]
2871 fn test_new() {
2872 let r = Relation::new(
2873 "samba",
2874 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
2875 );
2876
2877 assert_eq!(r.to_string(), "samba (>= 2.0)");
2878 }
2879
2880 #[test]
2881 fn test_drop_constraint() {
2882 let mut r = Relation::new(
2883 "samba",
2884 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
2885 );
2886
2887 r.drop_constraint();
2888
2889 assert_eq!(r.to_string(), "samba");
2890 }
2891
2892 #[test]
2893 fn test_simple() {
2894 let r = Relation::simple("samba");
2895
2896 assert_eq!(r.to_string(), "samba");
2897 }
2898
2899 #[test]
2900 fn test_remove_first_entry() {
2901 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
2902 .parse()
2903 .unwrap();
2904 let removed = rels.remove_entry(0);
2905 assert_eq!(removed.to_string(), "python3-dulwich (>= 0.20.21)");
2906 assert_eq!(rels.to_string(), "python3-dulwich (<< 0.21)");
2907 }
2908
2909 #[test]
2910 fn test_remove_last_entry() {
2911 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
2912 .parse()
2913 .unwrap();
2914 rels.remove_entry(1);
2915 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
2916 }
2917
2918 #[test]
2919 fn test_remove_middle() {
2920 let mut rels: Relations =
2921 r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21), python3-dulwich (<< 0.22)"#
2922 .parse()
2923 .unwrap();
2924 rels.remove_entry(1);
2925 assert_eq!(
2926 rels.to_string(),
2927 "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.22)"
2928 );
2929 }
2930
2931 #[test]
2932 fn test_remove_added() {
2933 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
2934 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
2935 rels.push(entry);
2936 rels.remove_entry(1);
2937 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
2938 }
2939
2940 #[test]
2941 fn test_push() {
2942 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
2943 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
2944 rels.push(entry);
2945 assert_eq!(
2946 rels.to_string(),
2947 "python3-dulwich (>= 0.20.21), python3-dulwich"
2948 );
2949 }
2950
2951 #[test]
2952 fn test_insert_with_custom_separator() {
2953 let mut rels: Relations = "python3".parse().unwrap();
2954 let entry = Entry::from(vec![Relation::simple("debhelper")]);
2955 rels.insert_with_separator(1, entry, Some("\n "));
2956 assert_eq!(rels.to_string(), "python3,\n debhelper");
2957 }
2958
2959 #[test]
2960 fn test_insert_with_wrap_and_sort_separator() {
2961 let mut rels: Relations = "python3".parse().unwrap();
2962 let entry = Entry::from(vec![Relation::simple("rustc")]);
2963 rels.insert_with_separator(1, entry, Some("\n "));
2965 assert_eq!(rels.to_string(), "python3,\n rustc");
2966 }
2967
2968 #[test]
2969 fn test_push_from_empty() {
2970 let mut rels: Relations = "".parse().unwrap();
2971 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
2972 rels.push(entry);
2973 assert_eq!(rels.to_string(), "python3-dulwich");
2974 }
2975
2976 #[test]
2977 fn test_insert() {
2978 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
2979 .parse()
2980 .unwrap();
2981 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
2982 rels.insert(1, entry);
2983 assert_eq!(
2984 rels.to_string(),
2985 "python3-dulwich (>= 0.20.21), python3-dulwich, python3-dulwich (<< 0.21)"
2986 );
2987 }
2988
2989 #[test]
2990 fn test_insert_at_start() {
2991 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
2992 .parse()
2993 .unwrap();
2994 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
2995 rels.insert(0, entry);
2996 assert_eq!(
2997 rels.to_string(),
2998 "python3-dulwich, python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
2999 );
3000 }
3001
3002 #[test]
3003 fn test_insert_after_error() {
3004 let (mut rels, errors) = Relations::parse_relaxed("@foo@, debhelper (>= 1.0)", false);
3005 assert_eq!(
3006 errors,
3007 vec![
3008 "expected $ or identifier but got ERROR",
3009 "expected comma or end of file but got Some(IDENT)",
3010 "expected $ or identifier but got ERROR"
3011 ]
3012 );
3013 let entry = Entry::from(vec![Relation::simple("bar")]);
3014 rels.push(entry);
3015 assert_eq!(rels.to_string(), "@foo@, debhelper (>= 1.0), bar");
3016 }
3017
3018 #[test]
3019 fn test_insert_before_error() {
3020 let (mut rels, errors) = Relations::parse_relaxed("debhelper (>= 1.0), @foo@, bla", false);
3021 assert_eq!(
3022 errors,
3023 vec![
3024 "expected $ or identifier but got ERROR",
3025 "expected comma or end of file but got Some(IDENT)",
3026 "expected $ or identifier but got ERROR"
3027 ]
3028 );
3029 let entry = Entry::from(vec![Relation::simple("bar")]);
3030 rels.insert(0, entry);
3031 assert_eq!(rels.to_string(), "bar, debhelper (>= 1.0), @foo@, bla");
3032 }
3033
3034 #[test]
3035 fn test_replace() {
3036 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3037 .parse()
3038 .unwrap();
3039 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3040 rels.replace(1, entry);
3041 assert_eq!(
3042 rels.to_string(),
3043 "python3-dulwich (>= 0.20.21), python3-dulwich"
3044 );
3045 }
3046
3047 #[test]
3048 fn test_relation_from_entries() {
3049 let entries = vec![
3050 Entry::from(vec![Relation::simple("python3-dulwich")]),
3051 Entry::from(vec![Relation::simple("python3-breezy")]),
3052 ];
3053 let rels: Relations = entries.into();
3054 assert_eq!(rels.entries().count(), 2);
3055 assert_eq!(rels.to_string(), "python3-dulwich, python3-breezy");
3056 }
3057
3058 #[test]
3059 fn test_entry_from_relations() {
3060 let relations = vec![
3061 Relation::simple("python3-dulwich"),
3062 Relation::simple("python3-breezy"),
3063 ];
3064 let entry: Entry = relations.into();
3065 assert_eq!(entry.relations().count(), 2);
3066 assert_eq!(entry.to_string(), "python3-dulwich | python3-breezy");
3067 }
3068
3069 #[test]
3070 fn test_parse_entry() {
3071 let parsed: Entry = "python3-dulwich (>= 0.20.21) | bar".parse().unwrap();
3072 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21) | bar");
3073 assert_eq!(parsed.relations().count(), 2);
3074
3075 assert_eq!(
3076 "foo, bar".parse::<Entry>().unwrap_err(),
3077 "Multiple entries found"
3078 );
3079 assert_eq!("".parse::<Entry>().unwrap_err(), "No entry found");
3080 }
3081
3082 #[test]
3083 fn test_parse_relation() {
3084 let parsed: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3085 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21)");
3086 assert_eq!(
3087 parsed.version(),
3088 Some((
3089 VersionConstraint::GreaterThanEqual,
3090 "0.20.21".parse().unwrap()
3091 ))
3092 );
3093 assert_eq!(
3094 "foo | bar".parse::<Relation>().unwrap_err(),
3095 "Multiple relations found"
3096 );
3097 assert_eq!("".parse::<Relation>().unwrap_err(), "No entry found");
3098 }
3099
3100 #[test]
3101 fn test_special() {
3102 let parsed: Relation = "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
3103 .parse()
3104 .unwrap();
3105 assert_eq!(
3106 parsed.to_string(),
3107 "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
3108 );
3109 assert_eq!(
3110 parsed.version(),
3111 Some((
3112 VersionConstraint::GreaterThanEqual,
3113 "0.1.138-~~".parse().unwrap()
3114 ))
3115 );
3116 assert_eq!(parsed.archqual(), Some("amd64".to_string()));
3117 assert_eq!(parsed.name(), "librust-breezyshim+dirty-tracker-dev");
3118 }
3119
3120 #[test]
3121 fn test_relations_satisfied_by() {
3122 let rels: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3123 .parse()
3124 .unwrap();
3125 let satisfied = |name: &str| -> Option<debversion::Version> {
3126 match name {
3127 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
3128 _ => None,
3129 }
3130 };
3131 assert!(rels.satisfied_by(satisfied));
3132
3133 let satisfied = |name: &str| match name {
3134 "python3-dulwich" => Some("0.21".parse().unwrap()),
3135 _ => None,
3136 };
3137 assert!(!rels.satisfied_by(satisfied));
3138
3139 let satisfied = |name: &str| match name {
3140 "python3-dulwich" => Some("0.20.20".parse().unwrap()),
3141 _ => None,
3142 };
3143 assert!(!rels.satisfied_by(satisfied));
3144 }
3145
3146 #[test]
3147 fn test_entry_satisfied_by() {
3148 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3149 .parse()
3150 .unwrap();
3151 let satisfied = |name: &str| -> Option<debversion::Version> {
3152 match name {
3153 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
3154 _ => None,
3155 }
3156 };
3157 assert!(entry.satisfied_by(satisfied));
3158 let satisfied = |name: &str| -> Option<debversion::Version> {
3159 match name {
3160 "python3-dulwich" => Some("0.18".parse().unwrap()),
3161 _ => None,
3162 }
3163 };
3164 assert!(!entry.satisfied_by(satisfied));
3165 }
3166
3167 #[test]
3168 fn test_wrap_and_sort_relation() {
3169 let relation: Relation = " python3-dulwich (>= 11) [ amd64 ] < lala>"
3170 .parse()
3171 .unwrap();
3172
3173 let wrapped = relation.wrap_and_sort();
3174
3175 assert_eq!(
3176 wrapped.to_string(),
3177 "python3-dulwich (>= 11) [amd64] <lala>"
3178 );
3179 }
3180
3181 #[test]
3182 fn test_wrap_and_sort_relations() {
3183 let entry: Relations =
3184 "python3-dulwich (>= 0.20.21) | bar, \n\n\n\npython3-dulwich (<< 0.21)"
3185 .parse()
3186 .unwrap();
3187
3188 let wrapped = entry.wrap_and_sort();
3189
3190 assert_eq!(
3191 wrapped.to_string(),
3192 "bar | python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3193 );
3194 }
3195
3196 #[cfg(feature = "serde")]
3197 #[test]
3198 fn test_serialize_relations() {
3199 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3200 .parse()
3201 .unwrap();
3202 let serialized = serde_json::to_string(&relations).unwrap();
3203 assert_eq!(
3204 serialized,
3205 r#""python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)""#
3206 );
3207 }
3208
3209 #[cfg(feature = "serde")]
3210 #[test]
3211 fn test_deserialize_relations() {
3212 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3213 .parse()
3214 .unwrap();
3215 let serialized = serde_json::to_string(&relations).unwrap();
3216 let deserialized: Relations = serde_json::from_str(&serialized).unwrap();
3217 assert_eq!(deserialized.to_string(), relations.to_string());
3218 }
3219
3220 #[cfg(feature = "serde")]
3221 #[test]
3222 fn test_serialize_relation() {
3223 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3224 let serialized = serde_json::to_string(&relation).unwrap();
3225 assert_eq!(serialized, r#""python3-dulwich (>= 0.20.21)""#);
3226 }
3227
3228 #[cfg(feature = "serde")]
3229 #[test]
3230 fn test_deserialize_relation() {
3231 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3232 let serialized = serde_json::to_string(&relation).unwrap();
3233 let deserialized: Relation = serde_json::from_str(&serialized).unwrap();
3234 assert_eq!(deserialized.to_string(), relation.to_string());
3235 }
3236
3237 #[cfg(feature = "serde")]
3238 #[test]
3239 fn test_serialize_entry() {
3240 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3241 .parse()
3242 .unwrap();
3243 let serialized = serde_json::to_string(&entry).unwrap();
3244 assert_eq!(
3245 serialized,
3246 r#""python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)""#
3247 );
3248 }
3249
3250 #[cfg(feature = "serde")]
3251 #[test]
3252 fn test_deserialize_entry() {
3253 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3254 .parse()
3255 .unwrap();
3256 let serialized = serde_json::to_string(&entry).unwrap();
3257 let deserialized: Entry = serde_json::from_str(&serialized).unwrap();
3258 assert_eq!(deserialized.to_string(), entry.to_string());
3259 }
3260
3261 #[test]
3262 fn test_remove_first_relation() {
3263 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3264 .parse()
3265 .unwrap();
3266 let mut rel = entry.relations().next().unwrap();
3267 rel.remove();
3268 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.18)");
3269 }
3270
3271 #[test]
3272 fn test_remove_last_relation() {
3273 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3274 .parse()
3275 .unwrap();
3276 let mut rel = entry.relations().nth(1).unwrap();
3277 rel.remove();
3278 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3279 }
3280
3281 #[test]
3282 fn test_remove_only_relation() {
3283 let entry: Entry = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3284 let mut rel = entry.relations().next().unwrap();
3285 rel.remove();
3286 assert_eq!(entry.to_string(), "");
3287 }
3288
3289 #[test]
3290 fn test_relations_is_empty() {
3291 let entry: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3292 assert!(!entry.is_empty());
3293 assert_eq!(1, entry.len());
3294 let mut rel = entry.entries().next().unwrap();
3295 rel.remove();
3296 assert!(entry.is_empty());
3297 assert_eq!(0, entry.len());
3298 }
3299
3300 #[test]
3301 fn test_entry_is_empty() {
3302 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3303 .parse()
3304 .unwrap();
3305 assert!(!entry.is_empty());
3306 assert_eq!(2, entry.len());
3307 let mut rel = entry.relations().next().unwrap();
3308 rel.remove();
3309 assert!(!entry.is_empty());
3310 assert_eq!(1, entry.len());
3311 let mut rel = entry.relations().next().unwrap();
3312 rel.remove();
3313 assert!(entry.is_empty());
3314 assert_eq!(0, entry.len());
3315 }
3316
3317 #[test]
3318 fn test_relation_set_version() {
3319 let mut rel: Relation = "samba".parse().unwrap();
3320 rel.set_version(None);
3321 assert_eq!("samba", rel.to_string());
3322 rel.set_version(Some((
3323 VersionConstraint::GreaterThanEqual,
3324 "2.0".parse().unwrap(),
3325 )));
3326 assert_eq!("samba (>= 2.0)", rel.to_string());
3327 rel.set_version(None);
3328 assert_eq!("samba", rel.to_string());
3329 rel.set_version(Some((
3330 VersionConstraint::GreaterThanEqual,
3331 "2.0".parse().unwrap(),
3332 )));
3333 rel.set_version(Some((
3334 VersionConstraint::GreaterThanEqual,
3335 "1.1".parse().unwrap(),
3336 )));
3337 assert_eq!("samba (>= 1.1)", rel.to_string());
3338 }
3339
3340 #[test]
3341 fn test_replace_relation() {
3342 let mut entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3343 .parse()
3344 .unwrap();
3345 let new_rel = Relation::simple("python3-breezy");
3346 entry.replace(0, new_rel);
3347 assert_eq!(
3348 entry.to_string(),
3349 "python3-breezy | python3-dulwich (<< 0.18)"
3350 );
3351 }
3352
3353 #[test]
3354 fn test_entry_push_relation() {
3355 let relations: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3356 let new_rel = Relation::simple("python3-breezy");
3357 let mut entry = relations.entries().next().unwrap();
3358 entry.push(new_rel);
3359 assert_eq!(
3360 entry.to_string(),
3361 "python3-dulwich (>= 0.20.21) | python3-breezy"
3362 );
3363 assert_eq!(
3364 relations.to_string(),
3365 "python3-dulwich (>= 0.20.21) | python3-breezy"
3366 );
3367 }
3368
3369 #[test]
3370 fn test_relations_remove_empty_entry() {
3371 let (mut relations, errors) = Relations::parse_relaxed("foo, , bar, ", false);
3372 assert_eq!(errors, Vec::<String>::new());
3373 assert_eq!(relations.to_string(), "foo, , bar, ");
3374 assert_eq!(relations.len(), 2);
3375 assert_eq!(
3376 relations.entries().next().unwrap().to_string(),
3377 "foo".to_string()
3378 );
3379 assert_eq!(
3380 relations.entries().nth(1).unwrap().to_string(),
3381 "bar".to_string()
3382 );
3383 relations.remove_entry(1);
3384 assert_eq!(relations.to_string(), "foo, , ");
3385 }
3386
3387 #[test]
3388 fn test_entry_remove_relation() {
3389 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
3390 let removed = entry.remove_relation(0);
3391 assert_eq!(removed.to_string(), "python3-dulwich");
3392 assert_eq!(entry.to_string(), "samba");
3393 }
3394
3395 #[test]
3396 fn test_wrap_and_sort_removes_empty_entries() {
3397 let relations: Relations = "foo, , bar, ".parse().unwrap();
3398 let wrapped = relations.wrap_and_sort();
3399 assert_eq!(wrapped.to_string(), "bar, foo");
3400 }
3401
3402 #[test]
3403 fn test_set_archqual() {
3404 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
3405 let mut rel = entry.relations().next().unwrap();
3406 rel.set_archqual("amd64");
3407 assert_eq!(rel.to_string(), "python3-dulwich:amd64");
3408 assert_eq!(rel.archqual(), Some("amd64".to_string()));
3409 assert_eq!(entry.to_string(), "python3-dulwich:amd64 | samba");
3410 rel.set_archqual("i386");
3411 assert_eq!(rel.to_string(), "python3-dulwich:i386");
3412 assert_eq!(rel.archqual(), Some("i386".to_string()));
3413 assert_eq!(entry.to_string(), "python3-dulwich:i386 | samba");
3414 }
3415
3416 #[test]
3417 fn test_set_architectures() {
3418 let mut relation = Relation::simple("samba");
3419 relation.set_architectures(vec!["amd64", "i386"].into_iter());
3420 assert_eq!(relation.to_string(), "samba [amd64 i386]");
3421 }
3422
3423 #[test]
3424 fn test_ensure_minimum_version_add_new() {
3425 let mut relations: Relations = "python3".parse().unwrap();
3426 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3427 assert_eq!(relations.to_string(), "python3, debhelper (>= 12)");
3428 }
3429
3430 #[test]
3431 fn test_ensure_minimum_version_update() {
3432 let mut relations: Relations = "debhelper (>= 9)".parse().unwrap();
3433 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3434 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3435 }
3436
3437 #[test]
3438 fn test_ensure_minimum_version_no_change() {
3439 let mut relations: Relations = "debhelper (>= 13)".parse().unwrap();
3440 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3441 assert_eq!(relations.to_string(), "debhelper (>= 13)");
3442 }
3443
3444 #[test]
3445 fn test_ensure_minimum_version_no_version() {
3446 let mut relations: Relations = "debhelper".parse().unwrap();
3447 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3448 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3449 }
3450
3451 #[test]
3452 fn test_ensure_exact_version_add_new() {
3453 let mut relations: Relations = "python3".parse().unwrap();
3454 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3455 assert_eq!(relations.to_string(), "python3, debhelper (= 12)");
3456 }
3457
3458 #[test]
3459 fn test_ensure_exact_version_update() {
3460 let mut relations: Relations = "debhelper (>= 9)".parse().unwrap();
3461 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3462 assert_eq!(relations.to_string(), "debhelper (= 12)");
3463 }
3464
3465 #[test]
3466 fn test_ensure_exact_version_no_change() {
3467 let mut relations: Relations = "debhelper (= 12)".parse().unwrap();
3468 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3469 assert_eq!(relations.to_string(), "debhelper (= 12)");
3470 }
3471
3472 #[test]
3473 fn test_ensure_some_version_add_new() {
3474 let mut relations: Relations = "python3".parse().unwrap();
3475 relations.ensure_some_version("debhelper");
3476 assert_eq!(relations.to_string(), "python3, debhelper");
3477 }
3478
3479 #[test]
3480 fn test_ensure_some_version_exists_with_version() {
3481 let mut relations: Relations = "debhelper (>= 12)".parse().unwrap();
3482 relations.ensure_some_version("debhelper");
3483 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3484 }
3485
3486 #[test]
3487 fn test_ensure_some_version_exists_no_version() {
3488 let mut relations: Relations = "debhelper".parse().unwrap();
3489 relations.ensure_some_version("debhelper");
3490 assert_eq!(relations.to_string(), "debhelper");
3491 }
3492
3493 #[test]
3494 fn test_filter_entries_basic() {
3495 let mut relations: Relations = "python3, debhelper, rustc".parse().unwrap();
3496 relations.filter_entries(|entry| entry.relations().any(|r| r.name().starts_with("python")));
3497 assert_eq!(relations.to_string(), "python3");
3498 }
3499
3500 #[test]
3501 fn test_filter_entries_keep_all() {
3502 let mut relations: Relations = "python3, debhelper".parse().unwrap();
3503 relations.filter_entries(|_| true);
3504 assert_eq!(relations.to_string(), "python3, debhelper");
3505 }
3506
3507 #[test]
3508 fn test_filter_entries_remove_all() {
3509 let mut relations: Relations = "python3, debhelper".parse().unwrap();
3510 relations.filter_entries(|_| false);
3511 assert_eq!(relations.to_string(), "");
3512 }
3513
3514 #[test]
3515 fn test_filter_entries_keep_middle() {
3516 let mut relations: Relations = "aaa, bbb, ccc".parse().unwrap();
3517 relations.filter_entries(|entry| entry.relations().any(|r| r.name() == "bbb"));
3518 assert_eq!(relations.to_string(), "bbb");
3519 }
3520
3521 #[test]
3524 fn test_is_sorted_wrap_and_sort_order() {
3525 let relations: Relations = "debhelper, python3, rustc".parse().unwrap();
3527 assert!(relations.is_sorted(&WrapAndSortOrder));
3528
3529 let relations: Relations = "rustc, debhelper, python3".parse().unwrap();
3531 assert!(!relations.is_sorted(&WrapAndSortOrder));
3532
3533 let (relations, _) =
3535 Relations::parse_relaxed("cdbs, debhelper-compat, python3, ${misc:Depends}", true);
3536 assert!(relations.is_sorted(&WrapAndSortOrder));
3537 }
3538
3539 #[test]
3540 fn test_is_sorted_default_order() {
3541 let relations: Relations = "aaa, bbb, ccc".parse().unwrap();
3543 assert!(relations.is_sorted(&DefaultSortingOrder));
3544
3545 let relations: Relations = "ccc, aaa, bbb".parse().unwrap();
3547 assert!(!relations.is_sorted(&DefaultSortingOrder));
3548
3549 let (relations, _) = Relations::parse_relaxed("aaa, bbb, ${misc:Depends}", true);
3551 assert!(relations.is_sorted(&DefaultSortingOrder));
3552 }
3553
3554 #[test]
3555 fn test_is_sorted_with_substvars() {
3556 let (relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}, rustc", true);
3558 assert!(relations.is_sorted(&DefaultSortingOrder));
3560 }
3561
3562 #[test]
3563 fn test_drop_dependency_exists() {
3564 let mut relations: Relations = "python3, debhelper, rustc".parse().unwrap();
3565 assert!(relations.drop_dependency("debhelper"));
3566 assert_eq!(relations.to_string(), "python3, rustc");
3567 }
3568
3569 #[test]
3570 fn test_drop_dependency_not_exists() {
3571 let mut relations: Relations = "python3, rustc".parse().unwrap();
3572 assert!(!relations.drop_dependency("nonexistent"));
3573 assert_eq!(relations.to_string(), "python3, rustc");
3574 }
3575
3576 #[test]
3577 fn test_drop_dependency_only_item() {
3578 let mut relations: Relations = "python3".parse().unwrap();
3579 assert!(relations.drop_dependency("python3"));
3580 assert_eq!(relations.to_string(), "");
3581 }
3582
3583 #[test]
3584 fn test_add_dependency_to_empty() {
3585 let mut relations: Relations = "".parse().unwrap();
3586 let entry = Entry::from(Relation::simple("python3"));
3587 relations.add_dependency(entry, None);
3588 assert_eq!(relations.to_string(), "python3");
3589 }
3590
3591 #[test]
3592 fn test_add_dependency_sorted_position() {
3593 let mut relations: Relations = "debhelper, rustc".parse().unwrap();
3594 let entry = Entry::from(Relation::simple("python3"));
3595 relations.add_dependency(entry, None);
3596 assert_eq!(relations.to_string(), "debhelper, python3, rustc");
3598 }
3599
3600 #[test]
3601 fn test_add_dependency_explicit_position() {
3602 let mut relations: Relations = "python3, rustc".parse().unwrap();
3603 let entry = Entry::from(Relation::simple("debhelper"));
3604 relations.add_dependency(entry, Some(0));
3605 assert_eq!(relations.to_string(), "debhelper, python3, rustc");
3606 }
3607
3608 #[test]
3609 fn test_add_dependency_build_system_first() {
3610 let mut relations: Relations = "python3, rustc".parse().unwrap();
3611 let entry = Entry::from(Relation::simple("debhelper-compat"));
3612 relations.add_dependency(entry, None);
3613 assert_eq!(relations.to_string(), "debhelper-compat, python3, rustc");
3615 }
3616
3617 #[test]
3618 fn test_add_dependency_at_end() {
3619 let mut relations: Relations = "debhelper, python3".parse().unwrap();
3620 let entry = Entry::from(Relation::simple("zzz-package"));
3621 relations.add_dependency(entry, None);
3622 assert_eq!(relations.to_string(), "debhelper, python3, zzz-package");
3624 }
3625
3626 #[test]
3627 fn test_get_relation_exists() {
3628 let relations: Relations = "python3, debhelper (>= 12), rustc".parse().unwrap();
3629 let result = relations.get_relation("debhelper");
3630 assert!(result.is_ok());
3631 let (idx, entry) = result.unwrap();
3632 assert_eq!(idx, 1);
3633 assert_eq!(entry.to_string(), "debhelper (>= 12)");
3634 }
3635
3636 #[test]
3637 fn test_get_relation_not_exists() {
3638 let relations: Relations = "python3, rustc".parse().unwrap();
3639 let result = relations.get_relation("nonexistent");
3640 assert_eq!(result, Err("Package nonexistent not found".to_string()));
3641 }
3642
3643 #[test]
3644 fn test_get_relation_complex_rule() {
3645 let relations: Relations = "python3 | python3-minimal, rustc".parse().unwrap();
3646 let result = relations.get_relation("python3");
3647 assert_eq!(
3648 result,
3649 Err("Complex rule for python3, aborting".to_string())
3650 );
3651 }
3652
3653 #[test]
3654 fn test_iter_relations_for_simple() {
3655 let relations: Relations = "python3, debhelper, python3-dev".parse().unwrap();
3656 let entries: Vec<_> = relations.iter_relations_for("python3").collect();
3657 assert_eq!(entries.len(), 1);
3658 assert_eq!(entries[0].0, 0);
3659 assert_eq!(entries[0].1.to_string(), "python3");
3660 }
3661
3662 #[test]
3663 fn test_iter_relations_for_alternatives() {
3664 let relations: Relations = "python3 | python3-minimal, python3-dev".parse().unwrap();
3665 let entries: Vec<_> = relations.iter_relations_for("python3").collect();
3666 assert_eq!(entries.len(), 1);
3668 assert_eq!(entries[0].0, 0);
3669 }
3670
3671 #[test]
3672 fn test_iter_relations_for_not_found() {
3673 let relations: Relations = "python3, rustc".parse().unwrap();
3674 let entries: Vec<_> = relations.iter_relations_for("debhelper").collect();
3675 assert_eq!(entries.len(), 0);
3676 }
3677
3678 #[test]
3679 fn test_has_relation_exists() {
3680 let relations: Relations = "python3, debhelper, rustc".parse().unwrap();
3681 assert!(relations.has_relation("debhelper"));
3682 assert!(relations.has_relation("python3"));
3683 assert!(relations.has_relation("rustc"));
3684 }
3685
3686 #[test]
3687 fn test_has_relation_not_exists() {
3688 let relations: Relations = "python3, rustc".parse().unwrap();
3689 assert!(!relations.has_relation("debhelper"));
3690 }
3691
3692 #[test]
3693 fn test_has_relation_in_alternative() {
3694 let relations: Relations = "python3 | python3-minimal".parse().unwrap();
3695 assert!(relations.has_relation("python3"));
3696 assert!(relations.has_relation("python3-minimal"));
3697 }
3698
3699 #[test]
3700 fn test_sorting_order_wrap_and_sort_build_systems() {
3701 let order = WrapAndSortOrder;
3702 assert!(order.lt("debhelper", "python3"));
3704 assert!(order.lt("debhelper-compat", "rustc"));
3705 assert!(order.lt("cdbs", "aaa"));
3706 assert!(order.lt("dh-python", "python3"));
3707 }
3708
3709 #[test]
3710 fn test_sorting_order_wrap_and_sort_regular_packages() {
3711 let order = WrapAndSortOrder;
3712 assert!(order.lt("aaa", "bbb"));
3714 assert!(order.lt("python3", "rustc"));
3715 assert!(!order.lt("rustc", "python3"));
3716 }
3717
3718 #[test]
3719 fn test_sorting_order_wrap_and_sort_substvars() {
3720 let order = WrapAndSortOrder;
3721 assert!(order.lt("python3", "${misc:Depends}"));
3723 assert!(!order.lt("${misc:Depends}", "python3"));
3724 assert!(!order.ignore("${misc:Depends}"));
3726 }
3727
3728 #[test]
3729 fn test_sorting_order_default_special_items() {
3730 let order = DefaultSortingOrder;
3731 assert!(order.lt("python3", "${misc:Depends}"));
3733 assert!(order.lt("aaa", "@cdbs@"));
3734 assert!(order.ignore("${misc:Depends}"));
3736 assert!(order.ignore("@cdbs@"));
3737 assert!(!order.ignore("python3"));
3738 }
3739
3740 #[test]
3741 fn test_is_special_package_name() {
3742 assert!(is_special_package_name("${misc:Depends}"));
3743 assert!(is_special_package_name("${shlibs:Depends}"));
3744 assert!(is_special_package_name("@cdbs@"));
3745 assert!(!is_special_package_name("python3"));
3746 assert!(!is_special_package_name("debhelper"));
3747 }
3748
3749 #[test]
3750 fn test_add_dependency_with_explicit_position() {
3751 let mut relations: Relations = "python3, rustc".parse().unwrap();
3753 let entry = Entry::from(Relation::simple("debhelper"));
3754 relations.add_dependency(entry, Some(1));
3755 assert_eq!(relations.to_string(), "python3, debhelper, rustc");
3757 }
3758
3759 #[test]
3760 fn test_whitespace_detection_single_space() {
3761 let mut relations: Relations = "python3, rustc".parse().unwrap();
3762 let entry = Entry::from(Relation::simple("debhelper"));
3763 relations.add_dependency(entry, Some(1));
3764 assert_eq!(relations.to_string(), "python3, debhelper, rustc");
3765 }
3766
3767 #[test]
3768 fn test_whitespace_detection_multiple_spaces() {
3769 let mut relations: Relations = "python3, rustc, gcc".parse().unwrap();
3770 let entry = Entry::from(Relation::simple("debhelper"));
3771 relations.add_dependency(entry, Some(1));
3772 assert_eq!(relations.to_string(), "python3, debhelper, rustc, gcc");
3774 }
3775
3776 #[test]
3777 fn test_whitespace_detection_mixed_patterns() {
3778 let mut relations: Relations = "a, b, c, d, e".parse().unwrap();
3780 let entry = Entry::from(Relation::simple("x"));
3781 relations.push(entry);
3782 assert_eq!(relations.to_string(), "a, b, c, d, e, x");
3785 }
3786
3787 #[test]
3788 fn test_whitespace_detection_newlines() {
3789 let mut relations: Relations = "python3,\n rustc".parse().unwrap();
3790 let entry = Entry::from(Relation::simple("debhelper"));
3791 relations.add_dependency(entry, Some(1));
3792 assert_eq!(relations.to_string(), "python3,\n debhelper,\n rustc");
3794 }
3795
3796 #[test]
3797 fn test_append_with_newline_no_trailing() {
3798 let mut relations: Relations = "foo,\n bar".parse().unwrap();
3799 let entry = Entry::from(Relation::simple("blah"));
3800 relations.add_dependency(entry, None);
3801 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
3802 }
3803
3804 #[test]
3805 fn test_append_with_trailing_newline() {
3806 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
3807 let entry = Entry::from(Relation::simple("blah"));
3808 relations.add_dependency(entry, None);
3809 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
3810 }
3811
3812 #[test]
3813 fn test_append_with_4_space_indent() {
3814 let mut relations: Relations = "foo,\n bar".parse().unwrap();
3815 let entry = Entry::from(Relation::simple("blah"));
3816 relations.add_dependency(entry, None);
3817 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
3818 }
3819
3820 #[test]
3821 fn test_append_with_4_space_and_trailing_newline() {
3822 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
3823 let entry = Entry::from(Relation::simple("blah"));
3824 relations.add_dependency(entry, None);
3825 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
3826 }
3827
3828 #[test]
3829 fn test_odd_syntax_append_no_trailing() {
3830 let mut relations: Relations = "\n foo\n , bar".parse().unwrap();
3831 let entry = Entry::from(Relation::simple("blah"));
3832 relations.add_dependency(entry, None);
3833 assert_eq!(relations.to_string(), "\n foo\n , bar\n , blah");
3834 }
3835
3836 #[test]
3837 fn test_odd_syntax_append_with_trailing() {
3838 let mut relations: Relations = "\n foo\n , bar\n".parse().unwrap();
3839 let entry = Entry::from(Relation::simple("blah"));
3840 relations.add_dependency(entry, None);
3841 assert_eq!(relations.to_string(), "\n foo\n , bar\n , blah");
3842 }
3843
3844 #[test]
3845 fn test_insert_at_1_no_trailing() {
3846 let mut relations: Relations = "foo,\n bar".parse().unwrap();
3847 let entry = Entry::from(Relation::simple("blah"));
3848 relations.add_dependency(entry, Some(1));
3849 assert_eq!(relations.to_string(), "foo,\n blah,\n bar");
3850 }
3851
3852 #[test]
3853 fn test_insert_at_1_with_trailing() {
3854 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
3855 let entry = Entry::from(Relation::simple("blah"));
3856 relations.add_dependency(entry, Some(1));
3857 assert_eq!(relations.to_string(), "foo,\n blah,\n bar");
3858 }
3859
3860 #[test]
3861 fn test_odd_syntax_insert_at_1() {
3862 let mut relations: Relations = "\n foo\n , bar\n".parse().unwrap();
3863 let entry = Entry::from(Relation::simple("blah"));
3864 relations.add_dependency(entry, Some(1));
3865 assert_eq!(relations.to_string(), "\n foo\n , blah\n , bar");
3866 }
3867}