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 ensure_substvar(&mut self, substvar: &str) -> Result<(), String> {
1253 for existing in self.substvars() {
1255 if existing.trim() == substvar.trim() {
1256 return Ok(());
1257 }
1258 }
1259
1260 let (parsed, errors) = Relations::parse_relaxed(substvar, true);
1262 if !errors.is_empty() {
1263 return Err(errors.join("\n"));
1264 }
1265
1266 let whitespace = self.detect_whitespace_pattern(" ");
1268
1269 for substvar_node in parsed.0.children().filter(|n| n.kind() == SUBSTVAR) {
1271 let has_content = self.entries().next().is_some() || self.substvars().next().is_some();
1272
1273 let mut builder = GreenNodeBuilder::new();
1274 builder.start_node(ROOT.into());
1275
1276 for child in self.0.children_with_tokens() {
1278 match child {
1279 NodeOrToken::Node(n) => inject(&mut builder, n),
1280 NodeOrToken::Token(t) => builder.token(t.kind().into(), t.text()),
1281 }
1282 }
1283
1284 if has_content {
1286 builder.token(COMMA.into(), ",");
1287 builder.token(WHITESPACE.into(), whitespace.as_str());
1288 }
1289
1290 inject(&mut builder, substvar_node);
1292
1293 builder.finish_node();
1294 self.0 = SyntaxNode::new_root_mut(builder.finish());
1295 }
1296
1297 Ok(())
1298 }
1299
1300 pub fn filter_entries<F>(&mut self, keep: F)
1316 where
1317 F: Fn(&Entry) -> bool,
1318 {
1319 let indices_to_remove: Vec<_> = self
1320 .entries()
1321 .enumerate()
1322 .filter_map(|(idx, entry)| if keep(&entry) { None } else { Some(idx) })
1323 .collect();
1324
1325 for idx in indices_to_remove.into_iter().rev() {
1327 self.remove_entry(idx);
1328 }
1329 }
1330
1331 pub fn is_sorted(&self, sorting_order: &impl SortingOrder) -> bool {
1347 let mut last_name: Option<String> = None;
1348 for entry in self.entries() {
1349 let mut relations = entry.relations();
1351 let Some(relation) = relations.next() else {
1352 continue;
1353 };
1354
1355 let name = relation.name();
1356
1357 if sorting_order.ignore(&name) {
1359 continue;
1360 }
1361
1362 if let Some(ref last) = last_name {
1364 if sorting_order.lt(&name, last) {
1365 return false;
1366 }
1367 }
1368
1369 last_name = Some(name);
1370 }
1371 true
1372 }
1373
1374 fn find_insert_position(&self, entry: &Entry) -> usize {
1386 let Some(relation) = entry.relations().next() else {
1388 return self.len();
1390 };
1391 let package_name = relation.name();
1392
1393 let count = self.entries().filter(|e| !e.is_empty()).count();
1395
1396 let sorting_order: Box<dyn SortingOrder> = if count < 2 {
1398 Box::new(WrapAndSortOrder)
1399 } else {
1400 if self.is_sorted(&WrapAndSortOrder) {
1403 Box::new(WrapAndSortOrder)
1404 } else if self.is_sorted(&DefaultSortingOrder) {
1405 Box::new(DefaultSortingOrder)
1406 } else {
1407 return self.len();
1409 }
1410 };
1411
1412 if sorting_order.ignore(&package_name) {
1414 return self.len();
1415 }
1416
1417 let mut position = 0;
1419 for (idx, existing_entry) in self.entries().enumerate() {
1420 let mut existing_relations = existing_entry.relations();
1421 let Some(existing_relation) = existing_relations.next() else {
1422 position += 1;
1424 continue;
1425 };
1426
1427 let existing_name = existing_relation.name();
1428
1429 if sorting_order.ignore(&existing_name) {
1431 position += 1;
1432 continue;
1433 }
1434
1435 if sorting_order.lt(&package_name, &existing_name) {
1437 return idx;
1438 }
1439 position += 1;
1440 }
1441
1442 position
1443 }
1444
1445 pub fn drop_dependency(&mut self, package: &str) -> bool {
1463 let indices_to_remove: Vec<_> = self
1464 .entries()
1465 .enumerate()
1466 .filter_map(|(idx, entry)| {
1467 let relations: Vec<_> = entry.relations().collect();
1468 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1469 if names == vec![package] {
1470 Some(idx)
1471 } else {
1472 None
1473 }
1474 })
1475 .collect();
1476
1477 let found = !indices_to_remove.is_empty();
1478
1479 for idx in indices_to_remove.into_iter().rev() {
1481 self.remove_entry(idx);
1482 }
1483
1484 found
1485 }
1486
1487 pub fn add_dependency(&mut self, entry: Entry, position: Option<usize>) {
1508 let pos = position.unwrap_or_else(|| self.find_insert_position(&entry));
1509 self.insert(pos, entry);
1510 }
1511
1512 pub fn get_relation(&self, package: &str) -> Result<(usize, Entry), String> {
1536 for (idx, entry) in self.entries().enumerate() {
1537 let relations: Vec<_> = entry.relations().collect();
1538 let names: Vec<_> = relations.iter().map(|r| r.name()).collect();
1539
1540 if names.len() > 1 && names.contains(&package.to_string()) {
1541 return Err(format!("Complex rule for {}, aborting", package));
1542 }
1543
1544 if names.len() == 1 && names[0] == package {
1545 return Ok((idx, entry));
1546 }
1547 }
1548 Err(format!("Package {} not found", package))
1549 }
1550
1551 pub fn iter_relations_for(&self, package: &str) -> impl Iterator<Item = (usize, Entry)> + '_ {
1568 let package = package.to_string();
1569 self.entries().enumerate().filter(move |(_, entry)| {
1570 let names: Vec<_> = entry.relations().map(|r| r.name()).collect();
1571 names.contains(&package)
1572 })
1573 }
1574
1575 pub fn has_relation(&self, package: &str) -> bool {
1592 self.entries()
1593 .any(|entry| entry.relations().any(|r| r.name() == package))
1594 }
1595}
1596
1597impl From<Vec<Entry>> for Relations {
1598 fn from(entries: Vec<Entry>) -> Self {
1599 let mut builder = GreenNodeBuilder::new();
1600 builder.start_node(ROOT.into());
1601 for (i, entry) in entries.into_iter().enumerate() {
1602 if i > 0 {
1603 builder.token(COMMA.into(), ",");
1604 builder.token(WHITESPACE.into(), " ");
1605 }
1606 inject(&mut builder, entry.0);
1607 }
1608 builder.finish_node();
1609 Relations(SyntaxNode::new_root_mut(builder.finish()))
1610 }
1611}
1612
1613impl From<Entry> for Relations {
1614 fn from(entry: Entry) -> Self {
1615 Self::from(vec![entry])
1616 }
1617}
1618
1619impl Default for Entry {
1620 fn default() -> Self {
1621 Self::new()
1622 }
1623}
1624
1625impl PartialOrd for Entry {
1626 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1627 let mut rels_a = self.relations();
1628 let mut rels_b = other.relations();
1629 while let (Some(a), Some(b)) = (rels_a.next(), rels_b.next()) {
1630 match a.cmp(&b) {
1631 std::cmp::Ordering::Equal => continue,
1632 x => return Some(x),
1633 }
1634 }
1635
1636 if rels_a.next().is_some() {
1637 return Some(std::cmp::Ordering::Greater);
1638 }
1639
1640 if rels_b.next().is_some() {
1641 return Some(std::cmp::Ordering::Less);
1642 }
1643
1644 Some(std::cmp::Ordering::Equal)
1645 }
1646}
1647
1648impl Eq for Entry {}
1649
1650impl Ord for Entry {
1651 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1652 self.partial_cmp(other).unwrap()
1653 }
1654}
1655
1656impl Entry {
1657 pub fn new() -> Self {
1659 let mut builder = GreenNodeBuilder::new();
1660 builder.start_node(SyntaxKind::ENTRY.into());
1661 builder.finish_node();
1662 Entry(SyntaxNode::new_root_mut(builder.finish()))
1663 }
1664
1665 pub fn replace(&mut self, idx: usize, relation: Relation) {
1667 let current_relation = self.get_relation(idx).unwrap();
1668
1669 let old_root = current_relation.0;
1670 let new_root = relation.0;
1671 let mut prev = new_root.first_child_or_token();
1673 let mut new_head_len = 0;
1674 while let Some(p) = prev {
1676 if p.kind() == WHITESPACE || p.kind() == NEWLINE {
1677 new_head_len += 1;
1678 prev = p.next_sibling_or_token();
1679 } else {
1680 break;
1681 }
1682 }
1683 let mut new_tail_len = 0;
1684 let mut next = new_root.last_child_or_token();
1685 while let Some(n) = next {
1686 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1687 new_tail_len += 1;
1688 next = n.prev_sibling_or_token();
1689 } else {
1690 break;
1691 }
1692 }
1693 let mut prev = old_root.first_child_or_token();
1695 let mut old_head = vec![];
1696 while let Some(p) = prev {
1697 if p.kind() == WHITESPACE || p.kind() == NEWLINE {
1698 old_head.push(p.clone());
1699 prev = p.next_sibling_or_token();
1700 } else {
1701 break;
1702 }
1703 }
1704 let mut old_tail = vec![];
1705 let mut next = old_root.last_child_or_token();
1706 while let Some(n) = next {
1707 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1708 old_tail.push(n.clone());
1709 next = n.prev_sibling_or_token();
1710 } else {
1711 break;
1712 }
1713 }
1714 new_root.splice_children(0..new_head_len, old_head);
1715 let tail_pos = new_root.children_with_tokens().count() - new_tail_len;
1716 new_root.splice_children(
1717 tail_pos - new_tail_len..tail_pos,
1718 old_tail.into_iter().rev(),
1719 );
1720 let index = old_root.index();
1721 self.0
1722 .splice_children(index..index + 1, vec![new_root.into()]);
1723 }
1724
1725 #[must_use]
1727 pub fn wrap_and_sort(&self) -> Self {
1728 let mut relations = self
1729 .relations()
1730 .map(|r| r.wrap_and_sort())
1731 .collect::<Vec<_>>();
1732 relations.sort();
1734 Self::from(relations)
1735 }
1736
1737 pub fn relations(&self) -> impl Iterator<Item = Relation> + '_ {
1739 self.0.children().filter_map(Relation::cast)
1740 }
1741
1742 pub fn iter(&self) -> impl Iterator<Item = Relation> + '_ {
1744 self.relations()
1745 }
1746
1747 pub fn get_relation(&self, idx: usize) -> Option<Relation> {
1749 self.relations().nth(idx)
1750 }
1751
1752 pub fn remove_relation(&self, idx: usize) -> Relation {
1765 let mut relation = self.get_relation(idx).unwrap();
1766 relation.remove();
1767 relation
1768 }
1769
1770 pub fn satisfied_by(&self, package_version: impl crate::VersionLookup + Copy) -> bool {
1786 self.relations().any(|r| {
1787 let actual = package_version.lookup_version(r.name().as_str());
1788 if let Some((vc, version)) = r.version() {
1789 if let Some(actual) = actual {
1790 match vc {
1791 VersionConstraint::GreaterThanEqual => *actual >= version,
1792 VersionConstraint::LessThanEqual => *actual <= version,
1793 VersionConstraint::Equal => *actual == version,
1794 VersionConstraint::GreaterThan => *actual > version,
1795 VersionConstraint::LessThan => *actual < version,
1796 }
1797 } else {
1798 false
1799 }
1800 } else {
1801 actual.is_some()
1802 }
1803 })
1804 }
1805
1806 pub fn remove(&mut self) {
1817 let mut removed_comma = false;
1818 let is_first = !self
1819 .0
1820 .siblings(Direction::Prev)
1821 .skip(1)
1822 .any(|n| n.kind() == ENTRY);
1823 while let Some(n) = self.0.next_sibling_or_token() {
1824 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1825 n.detach();
1826 } else if n.kind() == COMMA {
1827 n.detach();
1828 removed_comma = true;
1829 break;
1830 } else {
1831 panic!("Unexpected node: {:?}", n);
1832 }
1833 }
1834 if !is_first {
1835 while let Some(n) = self.0.prev_sibling_or_token() {
1836 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1837 n.detach();
1838 } else if !removed_comma && n.kind() == COMMA {
1839 n.detach();
1840 break;
1841 } else {
1842 break;
1843 }
1844 }
1845 } else {
1846 while let Some(n) = self.0.next_sibling_or_token() {
1847 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1848 n.detach();
1849 } else {
1850 break;
1851 }
1852 }
1853 }
1854 self.0.detach();
1855 }
1856
1857 pub fn is_empty(&self) -> bool {
1859 self.relations().count() == 0
1860 }
1861
1862 pub fn len(&self) -> usize {
1864 self.relations().count()
1865 }
1866
1867 pub fn push(&mut self, relation: Relation) {
1880 let is_empty = !self
1881 .0
1882 .children_with_tokens()
1883 .any(|n| n.kind() == PIPE || n.kind() == RELATION);
1884
1885 let (position, new_children) = if let Some(current_relation) = self.relations().last() {
1886 let to_insert: Vec<NodeOrToken<GreenNode, GreenToken>> = if is_empty {
1887 vec![relation.0.green().into()]
1888 } else {
1889 vec![
1890 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
1891 NodeOrToken::Token(GreenToken::new(PIPE.into(), "|")),
1892 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
1893 relation.0.green().into(),
1894 ]
1895 };
1896
1897 (current_relation.0.index() + 1, to_insert)
1898 } else {
1899 let child_count = self.0.children_with_tokens().count();
1900 (
1901 child_count,
1902 if is_empty {
1903 vec![relation.0.green().into()]
1904 } else {
1905 vec![
1906 NodeOrToken::Token(GreenToken::new(PIPE.into(), "|")),
1907 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
1908 relation.0.green().into(),
1909 ]
1910 },
1911 )
1912 };
1913
1914 let new_root = SyntaxNode::new_root_mut(
1915 self.0.replace_with(
1916 self.0
1917 .green()
1918 .splice_children(position..position, new_children),
1919 ),
1920 );
1921
1922 if let Some(parent) = self.0.parent() {
1923 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
1924 self.0 = parent
1925 .children_with_tokens()
1926 .nth(self.0.index())
1927 .unwrap()
1928 .clone()
1929 .into_node()
1930 .unwrap();
1931 } else {
1932 self.0 = new_root;
1933 }
1934 }
1935}
1936
1937fn inject(builder: &mut GreenNodeBuilder, node: SyntaxNode) {
1938 builder.start_node(node.kind().into());
1939 for child in node.children_with_tokens() {
1940 match child {
1941 rowan::NodeOrToken::Node(child) => {
1942 inject(builder, child);
1943 }
1944 rowan::NodeOrToken::Token(token) => {
1945 builder.token(token.kind().into(), token.text());
1946 }
1947 }
1948 }
1949 builder.finish_node();
1950}
1951
1952impl From<Vec<Relation>> for Entry {
1953 fn from(relations: Vec<Relation>) -> Self {
1954 let mut builder = GreenNodeBuilder::new();
1955 builder.start_node(SyntaxKind::ENTRY.into());
1956 for (i, relation) in relations.into_iter().enumerate() {
1957 if i > 0 {
1958 builder.token(WHITESPACE.into(), " ");
1959 builder.token(COMMA.into(), "|");
1960 builder.token(WHITESPACE.into(), " ");
1961 }
1962 inject(&mut builder, relation.0);
1963 }
1964 builder.finish_node();
1965 Entry(SyntaxNode::new_root_mut(builder.finish()))
1966 }
1967}
1968
1969impl From<Relation> for Entry {
1970 fn from(relation: Relation) -> Self {
1971 Self::from(vec![relation])
1972 }
1973}
1974
1975impl Relation {
1976 pub fn new(name: &str, version_constraint: Option<(VersionConstraint, Version)>) -> Self {
1990 let mut builder = GreenNodeBuilder::new();
1991 builder.start_node(SyntaxKind::RELATION.into());
1992 builder.token(IDENT.into(), name);
1993 if let Some((vc, version)) = version_constraint {
1994 builder.token(WHITESPACE.into(), " ");
1995 builder.start_node(SyntaxKind::VERSION.into());
1996 builder.token(L_PARENS.into(), "(");
1997 builder.start_node(SyntaxKind::CONSTRAINT.into());
1998 for c in vc.to_string().chars() {
1999 builder.token(
2000 match c {
2001 '>' => R_ANGLE.into(),
2002 '<' => L_ANGLE.into(),
2003 '=' => EQUAL.into(),
2004 _ => unreachable!(),
2005 },
2006 c.to_string().as_str(),
2007 );
2008 }
2009 builder.finish_node();
2010
2011 builder.token(WHITESPACE.into(), " ");
2012
2013 builder.token(IDENT.into(), version.to_string().as_str());
2014
2015 builder.token(R_PARENS.into(), ")");
2016
2017 builder.finish_node();
2018 }
2019
2020 builder.finish_node();
2021 Relation(SyntaxNode::new_root_mut(builder.finish()))
2022 }
2023
2024 #[must_use]
2033 pub fn wrap_and_sort(&self) -> Self {
2034 let mut builder = GreenNodeBuilder::new();
2035 builder.start_node(SyntaxKind::RELATION.into());
2036 builder.token(IDENT.into(), self.name().as_str());
2037 if let Some(archqual) = self.archqual() {
2038 builder.token(COLON.into(), ":");
2039 builder.token(IDENT.into(), archqual.as_str());
2040 }
2041 if let Some((vc, version)) = self.version() {
2042 builder.token(WHITESPACE.into(), " ");
2043 builder.start_node(SyntaxKind::VERSION.into());
2044 builder.token(L_PARENS.into(), "(");
2045 builder.start_node(SyntaxKind::CONSTRAINT.into());
2046 builder.token(
2047 match vc {
2048 VersionConstraint::GreaterThanEqual => R_ANGLE.into(),
2049 VersionConstraint::LessThanEqual => L_ANGLE.into(),
2050 VersionConstraint::Equal => EQUAL.into(),
2051 VersionConstraint::GreaterThan => R_ANGLE.into(),
2052 VersionConstraint::LessThan => L_ANGLE.into(),
2053 },
2054 vc.to_string().as_str(),
2055 );
2056 builder.finish_node();
2057 builder.token(WHITESPACE.into(), " ");
2058 builder.token(IDENT.into(), version.to_string().as_str());
2059 builder.token(R_PARENS.into(), ")");
2060 builder.finish_node();
2061 }
2062 if let Some(architectures) = self.architectures() {
2063 builder.token(WHITESPACE.into(), " ");
2064 builder.start_node(ARCHITECTURES.into());
2065 builder.token(L_BRACKET.into(), "[");
2066 for (i, arch) in architectures.enumerate() {
2067 if i > 0 {
2068 builder.token(WHITESPACE.into(), " ");
2069 }
2070 builder.token(IDENT.into(), arch.as_str());
2071 }
2072 builder.token(R_BRACKET.into(), "]");
2073 builder.finish_node();
2074 }
2075 for profiles in self.profiles() {
2076 builder.token(WHITESPACE.into(), " ");
2077 builder.start_node(PROFILES.into());
2078 builder.token(L_ANGLE.into(), "<");
2079 for (i, profile) in profiles.into_iter().enumerate() {
2080 if i > 0 {
2081 builder.token(WHITESPACE.into(), " ");
2082 }
2083 match profile {
2084 BuildProfile::Disabled(name) => {
2085 builder.token(NOT.into(), "!");
2086 builder.token(IDENT.into(), name.as_str());
2087 }
2088 BuildProfile::Enabled(name) => {
2089 builder.token(IDENT.into(), name.as_str());
2090 }
2091 }
2092 }
2093 builder.token(R_ANGLE.into(), ">");
2094 builder.finish_node();
2095 }
2096 builder.finish_node();
2097 Relation(SyntaxNode::new_root_mut(builder.finish()))
2098 }
2099
2100 pub fn simple(name: &str) -> Self {
2109 Self::new(name, None)
2110 }
2111
2112 pub fn drop_constraint(&mut self) -> bool {
2123 let version_token = self.0.children().find(|n| n.kind() == VERSION);
2124 if let Some(version_token) = version_token {
2125 while let Some(prev) = version_token.prev_sibling_or_token() {
2127 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
2128 prev.detach();
2129 } else {
2130 break;
2131 }
2132 }
2133 version_token.detach();
2134 return true;
2135 }
2136
2137 false
2138 }
2139
2140 pub fn name(&self) -> String {
2149 self.0
2150 .children_with_tokens()
2151 .find_map(|it| match it {
2152 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2153 _ => None,
2154 })
2155 .unwrap()
2156 .text()
2157 .to_string()
2158 }
2159
2160 pub fn archqual(&self) -> Option<String> {
2169 let archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
2170 let node = if let Some(archqual) = archqual {
2171 archqual.children_with_tokens().find_map(|it| match it {
2172 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2173 _ => None,
2174 })
2175 } else {
2176 None
2177 };
2178 node.map(|n| n.text().to_string())
2179 }
2180
2181 pub fn set_archqual(&mut self, archqual: &str) {
2191 let mut builder = GreenNodeBuilder::new();
2192 builder.start_node(ARCHQUAL.into());
2193 builder.token(COLON.into(), ":");
2194 builder.token(IDENT.into(), archqual);
2195 builder.finish_node();
2196
2197 let node_archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
2198 if let Some(node_archqual) = node_archqual {
2199 self.0.splice_children(
2200 node_archqual.index()..node_archqual.index() + 1,
2201 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2202 );
2203 } else {
2204 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
2205 let idx = if let Some(name_node) = name_node {
2206 name_node.index() + 1
2207 } else {
2208 0
2209 };
2210 self.0.splice_children(
2211 idx..idx,
2212 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2213 );
2214 }
2215 }
2216
2217 pub fn version(&self) -> Option<(VersionConstraint, Version)> {
2219 let vc = self.0.children().find(|n| n.kind() == VERSION);
2220 let vc = vc.as_ref()?;
2221 let constraint = vc.children().find(|n| n.kind() == CONSTRAINT);
2222
2223 let version = vc.children_with_tokens().find_map(|it| match it {
2224 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
2225 _ => None,
2226 });
2227
2228 if let (Some(constraint), Some(version)) = (constraint, version) {
2229 let vc: VersionConstraint = constraint.to_string().parse().unwrap();
2230 Some((vc, (version.text().to_string()).parse().unwrap()))
2231 } else {
2232 None
2233 }
2234 }
2235
2236 pub fn set_version(&mut self, version_constraint: Option<(VersionConstraint, Version)>) {
2247 let current_version = self.0.children().find(|n| n.kind() == VERSION);
2248 if let Some((vc, version)) = version_constraint {
2249 let mut builder = GreenNodeBuilder::new();
2250 builder.start_node(VERSION.into());
2251 builder.token(L_PARENS.into(), "(");
2252 builder.start_node(CONSTRAINT.into());
2253 match vc {
2254 VersionConstraint::GreaterThanEqual => {
2255 builder.token(R_ANGLE.into(), ">");
2256 builder.token(EQUAL.into(), "=");
2257 }
2258 VersionConstraint::LessThanEqual => {
2259 builder.token(L_ANGLE.into(), "<");
2260 builder.token(EQUAL.into(), "=");
2261 }
2262 VersionConstraint::Equal => {
2263 builder.token(EQUAL.into(), "=");
2264 }
2265 VersionConstraint::GreaterThan => {
2266 builder.token(R_ANGLE.into(), ">");
2267 }
2268 VersionConstraint::LessThan => {
2269 builder.token(L_ANGLE.into(), "<");
2270 }
2271 }
2272 builder.finish_node(); builder.token(WHITESPACE.into(), " ");
2274 builder.token(IDENT.into(), version.to_string().as_str());
2275 builder.token(R_PARENS.into(), ")");
2276 builder.finish_node(); if let Some(current_version) = current_version {
2279 self.0.splice_children(
2280 current_version.index()..current_version.index() + 1,
2281 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
2282 );
2283 } else {
2284 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
2285 let idx = if let Some(name_node) = name_node {
2286 name_node.index() + 1
2287 } else {
2288 0
2289 };
2290 let new_children = vec![
2291 GreenToken::new(WHITESPACE.into(), " ").into(),
2292 builder.finish().into(),
2293 ];
2294 let new_root = SyntaxNode::new_root_mut(
2295 self.0.green().splice_children(idx..idx, new_children),
2296 );
2297 if let Some(parent) = self.0.parent() {
2298 parent
2299 .splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2300 self.0 = parent
2301 .children_with_tokens()
2302 .nth(self.0.index())
2303 .unwrap()
2304 .clone()
2305 .into_node()
2306 .unwrap();
2307 } else {
2308 self.0 = new_root;
2309 }
2310 }
2311 } else if let Some(current_version) = current_version {
2312 while let Some(prev) = current_version.prev_sibling_or_token() {
2314 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
2315 prev.detach();
2316 } else {
2317 break;
2318 }
2319 }
2320 current_version.detach();
2321 }
2322 }
2323
2324 pub fn architectures(&self) -> Option<impl Iterator<Item = String> + '_> {
2333 let architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES)?;
2334
2335 Some(architectures.children_with_tokens().filter_map(|node| {
2336 let token = node.as_token()?;
2337 if token.kind() == IDENT {
2338 Some(token.text().to_string())
2339 } else {
2340 None
2341 }
2342 }))
2343 }
2344
2345 pub fn profiles(&self) -> impl Iterator<Item = Vec<BuildProfile>> + '_ {
2355 let profiles = self.0.children().filter(|n| n.kind() == PROFILES);
2356
2357 profiles.map(|profile| {
2358 let mut ret = vec![];
2360 let mut current = vec![];
2361 for token in profile.children_with_tokens() {
2362 match token.kind() {
2363 WHITESPACE | NEWLINE => {
2364 if !current.is_empty() {
2365 ret.push(current.join("").parse::<BuildProfile>().unwrap());
2366 current = vec![];
2367 }
2368 }
2369 L_ANGLE | R_ANGLE => {}
2370 _ => {
2371 current.push(token.to_string());
2372 }
2373 }
2374 }
2375 if !current.is_empty() {
2376 ret.push(current.concat().parse().unwrap());
2377 }
2378 ret
2379 })
2380 }
2381
2382 pub fn remove(&mut self) {
2393 let is_first = !self
2394 .0
2395 .siblings(Direction::Prev)
2396 .skip(1)
2397 .any(|n| n.kind() == RELATION);
2398 if !is_first {
2399 while let Some(n) = self.0.prev_sibling_or_token() {
2402 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2403 n.detach();
2404 } else if n.kind() == PIPE {
2405 n.detach();
2406 break;
2407 } else {
2408 break;
2409 }
2410 }
2411 while let Some(n) = self.0.prev_sibling_or_token() {
2412 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2413 n.detach();
2414 } else {
2415 break;
2416 }
2417 }
2418 } else {
2419 while let Some(n) = self.0.next_sibling_or_token() {
2422 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2423 n.detach();
2424 } else if n.kind() == PIPE {
2425 n.detach();
2426 break;
2427 } else {
2428 panic!("Unexpected node: {:?}", n);
2429 }
2430 }
2431
2432 while let Some(n) = self.0.next_sibling_or_token() {
2433 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
2434 n.detach();
2435 } else {
2436 break;
2437 }
2438 }
2439 }
2440 if let Some(mut parent) = self.0.parent().and_then(Entry::cast) {
2442 if parent.is_empty() {
2443 parent.remove();
2444 } else {
2445 self.0.detach();
2446 }
2447 } else {
2448 self.0.detach();
2449 }
2450 }
2451
2452 pub fn set_architectures<'a>(&mut self, architectures: impl Iterator<Item = &'a str>) {
2462 let mut builder = GreenNodeBuilder::new();
2463 builder.start_node(ARCHITECTURES.into());
2464 builder.token(L_BRACKET.into(), "[");
2465 for (i, arch) in architectures.enumerate() {
2466 if i > 0 {
2467 builder.token(WHITESPACE.into(), " ");
2468 }
2469 builder.token(IDENT.into(), arch);
2470 }
2471 builder.token(R_BRACKET.into(), "]");
2472 builder.finish_node();
2473
2474 let node_architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES);
2475 if let Some(node_architectures) = node_architectures {
2476 let new_root = SyntaxNode::new_root_mut(builder.finish());
2477 self.0.splice_children(
2478 node_architectures.index()..node_architectures.index() + 1,
2479 vec![new_root.into()],
2480 );
2481 } else {
2482 let profiles = self.0.children().find(|n| n.kind() == PROFILES);
2483 let idx = if let Some(profiles) = profiles {
2484 profiles.index()
2485 } else {
2486 self.0.children_with_tokens().count()
2487 };
2488 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
2489 idx..idx,
2490 vec![
2491 GreenToken::new(WHITESPACE.into(), " ").into(),
2492 builder.finish().into(),
2493 ],
2494 ));
2495 if let Some(parent) = self.0.parent() {
2496 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2497 self.0 = parent
2498 .children_with_tokens()
2499 .nth(self.0.index())
2500 .unwrap()
2501 .clone()
2502 .into_node()
2503 .unwrap();
2504 } else {
2505 self.0 = new_root;
2506 }
2507 }
2508 }
2509
2510 pub fn add_profile(&mut self, profile: &[BuildProfile]) {
2521 let mut builder = GreenNodeBuilder::new();
2522 builder.start_node(PROFILES.into());
2523 builder.token(L_ANGLE.into(), "<");
2524 for (i, profile) in profile.iter().enumerate() {
2525 if i > 0 {
2526 builder.token(WHITESPACE.into(), " ");
2527 }
2528 match profile {
2529 BuildProfile::Disabled(name) => {
2530 builder.token(NOT.into(), "!");
2531 builder.token(IDENT.into(), name.as_str());
2532 }
2533 BuildProfile::Enabled(name) => {
2534 builder.token(IDENT.into(), name.as_str());
2535 }
2536 }
2537 }
2538 builder.token(R_ANGLE.into(), ">");
2539 builder.finish_node();
2540
2541 let node_profiles = self.0.children().find(|n| n.kind() == PROFILES);
2542 if let Some(node_profiles) = node_profiles {
2543 let new_root = SyntaxNode::new_root_mut(builder.finish());
2544 self.0.splice_children(
2545 node_profiles.index()..node_profiles.index() + 1,
2546 vec![new_root.into()],
2547 );
2548 } else {
2549 let idx = self.0.children_with_tokens().count();
2550 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
2551 idx..idx,
2552 vec![
2553 GreenToken::new(WHITESPACE.into(), " ").into(),
2554 builder.finish().into(),
2555 ],
2556 ));
2557 if let Some(parent) = self.0.parent() {
2558 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
2559 self.0 = parent
2560 .children_with_tokens()
2561 .nth(self.0.index())
2562 .unwrap()
2563 .clone()
2564 .into_node()
2565 .unwrap();
2566 } else {
2567 self.0 = new_root;
2568 }
2569 }
2570 }
2571
2572 pub fn build(name: &str) -> RelationBuilder {
2574 RelationBuilder::new(name)
2575 }
2576}
2577
2578pub struct RelationBuilder {
2592 name: String,
2593 version_constraint: Option<(VersionConstraint, Version)>,
2594 archqual: Option<String>,
2595 architectures: Option<Vec<String>>,
2596 profiles: Vec<Vec<BuildProfile>>,
2597}
2598
2599impl RelationBuilder {
2600 fn new(name: &str) -> Self {
2602 Self {
2603 name: name.to_string(),
2604 version_constraint: None,
2605 archqual: None,
2606 architectures: None,
2607 profiles: vec![],
2608 }
2609 }
2610
2611 pub fn version_constraint(mut self, vc: VersionConstraint, version: Version) -> Self {
2613 self.version_constraint = Some((vc, version));
2614 self
2615 }
2616
2617 pub fn archqual(mut self, archqual: &str) -> Self {
2619 self.archqual = Some(archqual.to_string());
2620 self
2621 }
2622
2623 pub fn architectures(mut self, architectures: Vec<String>) -> Self {
2625 self.architectures = Some(architectures);
2626 self
2627 }
2628
2629 pub fn profiles(mut self, profiles: Vec<Vec<BuildProfile>>) -> Self {
2631 self.profiles = profiles;
2632 self
2633 }
2634
2635 pub fn add_profile(mut self, profile: Vec<BuildProfile>) -> Self {
2637 self.profiles.push(profile);
2638 self
2639 }
2640
2641 pub fn build(self) -> Relation {
2643 let mut relation = Relation::new(&self.name, self.version_constraint);
2644 if let Some(archqual) = &self.archqual {
2645 relation.set_archqual(archqual);
2646 }
2647 if let Some(architectures) = &self.architectures {
2648 relation.set_architectures(architectures.iter().map(|s| s.as_str()));
2649 }
2650 for profile in &self.profiles {
2651 relation.add_profile(profile);
2652 }
2653 relation
2654 }
2655}
2656
2657impl PartialOrd for Relation {
2658 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
2659 let name_cmp = self.name().cmp(&other.name());
2661 if name_cmp != std::cmp::Ordering::Equal {
2662 return Some(name_cmp);
2663 }
2664
2665 let self_version = self.version();
2666 let other_version = other.version();
2667
2668 match (self_version, other_version) {
2669 (Some((self_vc, self_version)), Some((other_vc, other_version))) => {
2670 let vc_cmp = self_vc.cmp(&other_vc);
2671 if vc_cmp != std::cmp::Ordering::Equal {
2672 return Some(vc_cmp);
2673 }
2674
2675 Some(self_version.cmp(&other_version))
2676 }
2677 (Some(_), None) => Some(std::cmp::Ordering::Greater),
2678 (None, Some(_)) => Some(std::cmp::Ordering::Less),
2679 (None, None) => Some(std::cmp::Ordering::Equal),
2680 }
2681 }
2682}
2683
2684impl Eq for Relation {}
2685
2686impl Ord for Relation {
2687 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
2688 self.partial_cmp(other).unwrap()
2689 }
2690}
2691
2692impl std::str::FromStr for Relations {
2693 type Err = String;
2694
2695 fn from_str(s: &str) -> Result<Self, Self::Err> {
2696 let parse = parse(s, false);
2697 if parse.errors.is_empty() {
2698 Ok(parse.root_mut())
2699 } else {
2700 Err(parse.errors.join("\n"))
2701 }
2702 }
2703}
2704
2705impl std::str::FromStr for Entry {
2706 type Err = String;
2707
2708 fn from_str(s: &str) -> Result<Self, Self::Err> {
2709 let root: Relations = s.parse()?;
2710
2711 let mut entries = root.entries();
2712 let entry = if let Some(entry) = entries.next() {
2713 entry
2714 } else {
2715 return Err("No entry found".to_string());
2716 };
2717
2718 if entries.next().is_some() {
2719 return Err("Multiple entries found".to_string());
2720 }
2721
2722 Ok(entry)
2723 }
2724}
2725
2726impl std::str::FromStr for Relation {
2727 type Err = String;
2728
2729 fn from_str(s: &str) -> Result<Self, Self::Err> {
2730 let entry: Entry = s.parse()?;
2731
2732 let mut relations = entry.relations();
2733 let relation = if let Some(relation) = relations.next() {
2734 relation
2735 } else {
2736 return Err("No relation found".to_string());
2737 };
2738
2739 if relations.next().is_some() {
2740 return Err("Multiple relations found".to_string());
2741 }
2742
2743 Ok(relation)
2744 }
2745}
2746
2747impl From<crate::lossy::Relation> for Relation {
2748 fn from(relation: crate::lossy::Relation) -> Self {
2749 let mut builder = Relation::build(&relation.name);
2750
2751 if let Some((vc, version)) = relation.version {
2752 builder = builder.version_constraint(vc, version);
2753 }
2754
2755 if let Some(archqual) = relation.archqual {
2756 builder = builder.archqual(&archqual);
2757 }
2758
2759 if let Some(architectures) = relation.architectures {
2760 builder = builder.architectures(architectures);
2761 }
2762
2763 builder = builder.profiles(relation.profiles);
2764
2765 builder.build()
2766 }
2767}
2768
2769impl From<Relation> for crate::lossy::Relation {
2770 fn from(relation: Relation) -> Self {
2771 crate::lossy::Relation {
2772 name: relation.name(),
2773 version: relation.version(),
2774 archqual: relation.archqual(),
2775 architectures: relation.architectures().map(|a| a.collect()),
2776 profiles: relation.profiles().collect(),
2777 }
2778 }
2779}
2780
2781impl From<Entry> for Vec<crate::lossy::Relation> {
2782 fn from(entry: Entry) -> Self {
2783 entry.relations().map(|r| r.into()).collect()
2784 }
2785}
2786
2787impl From<Vec<crate::lossy::Relation>> for Entry {
2788 fn from(relations: Vec<crate::lossy::Relation>) -> Self {
2789 let relations: Vec<Relation> = relations.into_iter().map(|r| r.into()).collect();
2790 Entry::from(relations)
2791 }
2792}
2793
2794#[cfg(test)]
2795mod tests {
2796 use super::*;
2797
2798 #[test]
2799 fn test_parse() {
2800 let input = "python3-dulwich";
2801 let parsed: Relations = input.parse().unwrap();
2802 assert_eq!(parsed.to_string(), input);
2803 assert_eq!(parsed.entries().count(), 1);
2804 let entry = parsed.entries().next().unwrap();
2805 assert_eq!(entry.to_string(), "python3-dulwich");
2806 assert_eq!(entry.relations().count(), 1);
2807 let relation = entry.relations().next().unwrap();
2808 assert_eq!(relation.to_string(), "python3-dulwich");
2809 assert_eq!(relation.version(), None);
2810
2811 let input = "python3-dulwich (>= 0.20.21)";
2812 let parsed: Relations = input.parse().unwrap();
2813 assert_eq!(parsed.to_string(), input);
2814 assert_eq!(parsed.entries().count(), 1);
2815 let entry = parsed.entries().next().unwrap();
2816 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
2817 assert_eq!(entry.relations().count(), 1);
2818 let relation = entry.relations().next().unwrap();
2819 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
2820 assert_eq!(
2821 relation.version(),
2822 Some((
2823 VersionConstraint::GreaterThanEqual,
2824 "0.20.21".parse().unwrap()
2825 ))
2826 );
2827 }
2828
2829 #[test]
2830 fn test_multiple() {
2831 let input = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)";
2832 let parsed: Relations = input.parse().unwrap();
2833 assert_eq!(parsed.to_string(), input);
2834 assert_eq!(parsed.entries().count(), 2);
2835 let entry = parsed.entries().next().unwrap();
2836 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
2837 assert_eq!(entry.relations().count(), 1);
2838 let relation = entry.relations().next().unwrap();
2839 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
2840 assert_eq!(
2841 relation.version(),
2842 Some((
2843 VersionConstraint::GreaterThanEqual,
2844 "0.20.21".parse().unwrap()
2845 ))
2846 );
2847 let entry = parsed.entries().nth(1).unwrap();
2848 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.21)");
2849 assert_eq!(entry.relations().count(), 1);
2850 let relation = entry.relations().next().unwrap();
2851 assert_eq!(relation.to_string(), "python3-dulwich (<< 0.21)");
2852 assert_eq!(
2853 relation.version(),
2854 Some((VersionConstraint::LessThan, "0.21".parse().unwrap()))
2855 );
2856 }
2857
2858 #[test]
2859 fn test_architectures() {
2860 let input = "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]";
2861 let parsed: Relations = input.parse().unwrap();
2862 assert_eq!(parsed.to_string(), input);
2863 assert_eq!(parsed.entries().count(), 1);
2864 let entry = parsed.entries().next().unwrap();
2865 assert_eq!(
2866 entry.to_string(),
2867 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
2868 );
2869 assert_eq!(entry.relations().count(), 1);
2870 let relation = entry.relations().next().unwrap();
2871 assert_eq!(
2872 relation.to_string(),
2873 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
2874 );
2875 assert_eq!(relation.version(), None);
2876 assert_eq!(
2877 relation.architectures().unwrap().collect::<Vec<_>>(),
2878 vec![
2879 "amd64", "arm64", "armhf", "i386", "mips", "mips64el", "mipsel", "ppc64el", "s390x"
2880 ]
2881 .into_iter()
2882 .map(|s| s.to_string())
2883 .collect::<Vec<_>>()
2884 );
2885 }
2886
2887 #[test]
2888 fn test_profiles() {
2889 let input = "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>, bar";
2890 let parsed: Relations = input.parse().unwrap();
2891 assert_eq!(parsed.to_string(), input);
2892 assert_eq!(parsed.entries().count(), 2);
2893 let entry = parsed.entries().next().unwrap();
2894 assert_eq!(
2895 entry.to_string(),
2896 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
2897 );
2898 assert_eq!(entry.relations().count(), 1);
2899 let relation = entry.relations().next().unwrap();
2900 assert_eq!(
2901 relation.to_string(),
2902 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
2903 );
2904 assert_eq!(
2905 relation.version(),
2906 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap()))
2907 );
2908 assert_eq!(
2909 relation.architectures().unwrap().collect::<Vec<_>>(),
2910 vec!["i386", "arm"]
2911 .into_iter()
2912 .map(|s| s.to_string())
2913 .collect::<Vec<_>>()
2914 );
2915 assert_eq!(
2916 relation.profiles().collect::<Vec<_>>(),
2917 vec![
2918 vec![BuildProfile::Disabled("nocheck".to_string())],
2919 vec![BuildProfile::Disabled("cross".to_string())]
2920 ]
2921 );
2922 }
2923
2924 #[test]
2925 fn test_substvar() {
2926 let input = "${shlibs:Depends}";
2927
2928 let (parsed, errors) = Relations::parse_relaxed(input, true);
2929 assert_eq!(errors, Vec::<String>::new());
2930 assert_eq!(parsed.to_string(), input);
2931 assert_eq!(parsed.entries().count(), 0);
2932
2933 assert_eq!(
2934 parsed.substvars().collect::<Vec<_>>(),
2935 vec!["${shlibs:Depends}"]
2936 );
2937 }
2938
2939 #[test]
2940 fn test_new() {
2941 let r = Relation::new(
2942 "samba",
2943 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
2944 );
2945
2946 assert_eq!(r.to_string(), "samba (>= 2.0)");
2947 }
2948
2949 #[test]
2950 fn test_drop_constraint() {
2951 let mut r = Relation::new(
2952 "samba",
2953 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
2954 );
2955
2956 r.drop_constraint();
2957
2958 assert_eq!(r.to_string(), "samba");
2959 }
2960
2961 #[test]
2962 fn test_simple() {
2963 let r = Relation::simple("samba");
2964
2965 assert_eq!(r.to_string(), "samba");
2966 }
2967
2968 #[test]
2969 fn test_remove_first_entry() {
2970 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
2971 .parse()
2972 .unwrap();
2973 let removed = rels.remove_entry(0);
2974 assert_eq!(removed.to_string(), "python3-dulwich (>= 0.20.21)");
2975 assert_eq!(rels.to_string(), "python3-dulwich (<< 0.21)");
2976 }
2977
2978 #[test]
2979 fn test_remove_last_entry() {
2980 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
2981 .parse()
2982 .unwrap();
2983 rels.remove_entry(1);
2984 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
2985 }
2986
2987 #[test]
2988 fn test_remove_middle() {
2989 let mut rels: Relations =
2990 r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21), python3-dulwich (<< 0.22)"#
2991 .parse()
2992 .unwrap();
2993 rels.remove_entry(1);
2994 assert_eq!(
2995 rels.to_string(),
2996 "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.22)"
2997 );
2998 }
2999
3000 #[test]
3001 fn test_remove_added() {
3002 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
3003 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3004 rels.push(entry);
3005 rels.remove_entry(1);
3006 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
3007 }
3008
3009 #[test]
3010 fn test_push() {
3011 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
3012 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3013 rels.push(entry);
3014 assert_eq!(
3015 rels.to_string(),
3016 "python3-dulwich (>= 0.20.21), python3-dulwich"
3017 );
3018 }
3019
3020 #[test]
3021 fn test_insert_with_custom_separator() {
3022 let mut rels: Relations = "python3".parse().unwrap();
3023 let entry = Entry::from(vec![Relation::simple("debhelper")]);
3024 rels.insert_with_separator(1, entry, Some("\n "));
3025 assert_eq!(rels.to_string(), "python3,\n debhelper");
3026 }
3027
3028 #[test]
3029 fn test_insert_with_wrap_and_sort_separator() {
3030 let mut rels: Relations = "python3".parse().unwrap();
3031 let entry = Entry::from(vec![Relation::simple("rustc")]);
3032 rels.insert_with_separator(1, entry, Some("\n "));
3034 assert_eq!(rels.to_string(), "python3,\n rustc");
3035 }
3036
3037 #[test]
3038 fn test_push_from_empty() {
3039 let mut rels: Relations = "".parse().unwrap();
3040 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3041 rels.push(entry);
3042 assert_eq!(rels.to_string(), "python3-dulwich");
3043 }
3044
3045 #[test]
3046 fn test_insert() {
3047 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3048 .parse()
3049 .unwrap();
3050 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3051 rels.insert(1, entry);
3052 assert_eq!(
3053 rels.to_string(),
3054 "python3-dulwich (>= 0.20.21), python3-dulwich, python3-dulwich (<< 0.21)"
3055 );
3056 }
3057
3058 #[test]
3059 fn test_insert_at_start() {
3060 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3061 .parse()
3062 .unwrap();
3063 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3064 rels.insert(0, entry);
3065 assert_eq!(
3066 rels.to_string(),
3067 "python3-dulwich, python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3068 );
3069 }
3070
3071 #[test]
3072 fn test_insert_after_error() {
3073 let (mut rels, errors) = Relations::parse_relaxed("@foo@, debhelper (>= 1.0)", false);
3074 assert_eq!(
3075 errors,
3076 vec![
3077 "expected $ or identifier but got ERROR",
3078 "expected comma or end of file but got Some(IDENT)",
3079 "expected $ or identifier but got ERROR"
3080 ]
3081 );
3082 let entry = Entry::from(vec![Relation::simple("bar")]);
3083 rels.push(entry);
3084 assert_eq!(rels.to_string(), "@foo@, debhelper (>= 1.0), bar");
3085 }
3086
3087 #[test]
3088 fn test_insert_before_error() {
3089 let (mut rels, errors) = Relations::parse_relaxed("debhelper (>= 1.0), @foo@, bla", false);
3090 assert_eq!(
3091 errors,
3092 vec![
3093 "expected $ or identifier but got ERROR",
3094 "expected comma or end of file but got Some(IDENT)",
3095 "expected $ or identifier but got ERROR"
3096 ]
3097 );
3098 let entry = Entry::from(vec![Relation::simple("bar")]);
3099 rels.insert(0, entry);
3100 assert_eq!(rels.to_string(), "bar, debhelper (>= 1.0), @foo@, bla");
3101 }
3102
3103 #[test]
3104 fn test_replace() {
3105 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
3106 .parse()
3107 .unwrap();
3108 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
3109 rels.replace(1, entry);
3110 assert_eq!(
3111 rels.to_string(),
3112 "python3-dulwich (>= 0.20.21), python3-dulwich"
3113 );
3114 }
3115
3116 #[test]
3117 fn test_relation_from_entries() {
3118 let entries = vec![
3119 Entry::from(vec![Relation::simple("python3-dulwich")]),
3120 Entry::from(vec![Relation::simple("python3-breezy")]),
3121 ];
3122 let rels: Relations = entries.into();
3123 assert_eq!(rels.entries().count(), 2);
3124 assert_eq!(rels.to_string(), "python3-dulwich, python3-breezy");
3125 }
3126
3127 #[test]
3128 fn test_entry_from_relations() {
3129 let relations = vec![
3130 Relation::simple("python3-dulwich"),
3131 Relation::simple("python3-breezy"),
3132 ];
3133 let entry: Entry = relations.into();
3134 assert_eq!(entry.relations().count(), 2);
3135 assert_eq!(entry.to_string(), "python3-dulwich | python3-breezy");
3136 }
3137
3138 #[test]
3139 fn test_parse_entry() {
3140 let parsed: Entry = "python3-dulwich (>= 0.20.21) | bar".parse().unwrap();
3141 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21) | bar");
3142 assert_eq!(parsed.relations().count(), 2);
3143
3144 assert_eq!(
3145 "foo, bar".parse::<Entry>().unwrap_err(),
3146 "Multiple entries found"
3147 );
3148 assert_eq!("".parse::<Entry>().unwrap_err(), "No entry found");
3149 }
3150
3151 #[test]
3152 fn test_parse_relation() {
3153 let parsed: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3154 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21)");
3155 assert_eq!(
3156 parsed.version(),
3157 Some((
3158 VersionConstraint::GreaterThanEqual,
3159 "0.20.21".parse().unwrap()
3160 ))
3161 );
3162 assert_eq!(
3163 "foo | bar".parse::<Relation>().unwrap_err(),
3164 "Multiple relations found"
3165 );
3166 assert_eq!("".parse::<Relation>().unwrap_err(), "No entry found");
3167 }
3168
3169 #[test]
3170 fn test_special() {
3171 let parsed: Relation = "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
3172 .parse()
3173 .unwrap();
3174 assert_eq!(
3175 parsed.to_string(),
3176 "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
3177 );
3178 assert_eq!(
3179 parsed.version(),
3180 Some((
3181 VersionConstraint::GreaterThanEqual,
3182 "0.1.138-~~".parse().unwrap()
3183 ))
3184 );
3185 assert_eq!(parsed.archqual(), Some("amd64".to_string()));
3186 assert_eq!(parsed.name(), "librust-breezyshim+dirty-tracker-dev");
3187 }
3188
3189 #[test]
3190 fn test_relations_satisfied_by() {
3191 let rels: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3192 .parse()
3193 .unwrap();
3194 let satisfied = |name: &str| -> Option<debversion::Version> {
3195 match name {
3196 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
3197 _ => None,
3198 }
3199 };
3200 assert!(rels.satisfied_by(satisfied));
3201
3202 let satisfied = |name: &str| match name {
3203 "python3-dulwich" => Some("0.21".parse().unwrap()),
3204 _ => None,
3205 };
3206 assert!(!rels.satisfied_by(satisfied));
3207
3208 let satisfied = |name: &str| match name {
3209 "python3-dulwich" => Some("0.20.20".parse().unwrap()),
3210 _ => None,
3211 };
3212 assert!(!rels.satisfied_by(satisfied));
3213 }
3214
3215 #[test]
3216 fn test_entry_satisfied_by() {
3217 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3218 .parse()
3219 .unwrap();
3220 let satisfied = |name: &str| -> Option<debversion::Version> {
3221 match name {
3222 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
3223 _ => None,
3224 }
3225 };
3226 assert!(entry.satisfied_by(satisfied));
3227 let satisfied = |name: &str| -> Option<debversion::Version> {
3228 match name {
3229 "python3-dulwich" => Some("0.18".parse().unwrap()),
3230 _ => None,
3231 }
3232 };
3233 assert!(!entry.satisfied_by(satisfied));
3234 }
3235
3236 #[test]
3237 fn test_wrap_and_sort_relation() {
3238 let relation: Relation = " python3-dulwich (>= 11) [ amd64 ] < lala>"
3239 .parse()
3240 .unwrap();
3241
3242 let wrapped = relation.wrap_and_sort();
3243
3244 assert_eq!(
3245 wrapped.to_string(),
3246 "python3-dulwich (>= 11) [amd64] <lala>"
3247 );
3248 }
3249
3250 #[test]
3251 fn test_wrap_and_sort_relations() {
3252 let entry: Relations =
3253 "python3-dulwich (>= 0.20.21) | bar, \n\n\n\npython3-dulwich (<< 0.21)"
3254 .parse()
3255 .unwrap();
3256
3257 let wrapped = entry.wrap_and_sort();
3258
3259 assert_eq!(
3260 wrapped.to_string(),
3261 "bar | python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3262 );
3263 }
3264
3265 #[cfg(feature = "serde")]
3266 #[test]
3267 fn test_serialize_relations() {
3268 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3269 .parse()
3270 .unwrap();
3271 let serialized = serde_json::to_string(&relations).unwrap();
3272 assert_eq!(
3273 serialized,
3274 r#""python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)""#
3275 );
3276 }
3277
3278 #[cfg(feature = "serde")]
3279 #[test]
3280 fn test_deserialize_relations() {
3281 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
3282 .parse()
3283 .unwrap();
3284 let serialized = serde_json::to_string(&relations).unwrap();
3285 let deserialized: Relations = serde_json::from_str(&serialized).unwrap();
3286 assert_eq!(deserialized.to_string(), relations.to_string());
3287 }
3288
3289 #[cfg(feature = "serde")]
3290 #[test]
3291 fn test_serialize_relation() {
3292 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3293 let serialized = serde_json::to_string(&relation).unwrap();
3294 assert_eq!(serialized, r#""python3-dulwich (>= 0.20.21)""#);
3295 }
3296
3297 #[cfg(feature = "serde")]
3298 #[test]
3299 fn test_deserialize_relation() {
3300 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3301 let serialized = serde_json::to_string(&relation).unwrap();
3302 let deserialized: Relation = serde_json::from_str(&serialized).unwrap();
3303 assert_eq!(deserialized.to_string(), relation.to_string());
3304 }
3305
3306 #[cfg(feature = "serde")]
3307 #[test]
3308 fn test_serialize_entry() {
3309 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3310 .parse()
3311 .unwrap();
3312 let serialized = serde_json::to_string(&entry).unwrap();
3313 assert_eq!(
3314 serialized,
3315 r#""python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)""#
3316 );
3317 }
3318
3319 #[cfg(feature = "serde")]
3320 #[test]
3321 fn test_deserialize_entry() {
3322 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3323 .parse()
3324 .unwrap();
3325 let serialized = serde_json::to_string(&entry).unwrap();
3326 let deserialized: Entry = serde_json::from_str(&serialized).unwrap();
3327 assert_eq!(deserialized.to_string(), entry.to_string());
3328 }
3329
3330 #[test]
3331 fn test_remove_first_relation() {
3332 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3333 .parse()
3334 .unwrap();
3335 let mut rel = entry.relations().next().unwrap();
3336 rel.remove();
3337 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.18)");
3338 }
3339
3340 #[test]
3341 fn test_remove_last_relation() {
3342 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3343 .parse()
3344 .unwrap();
3345 let mut rel = entry.relations().nth(1).unwrap();
3346 rel.remove();
3347 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
3348 }
3349
3350 #[test]
3351 fn test_remove_only_relation() {
3352 let entry: Entry = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3353 let mut rel = entry.relations().next().unwrap();
3354 rel.remove();
3355 assert_eq!(entry.to_string(), "");
3356 }
3357
3358 #[test]
3359 fn test_relations_is_empty() {
3360 let entry: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3361 assert!(!entry.is_empty());
3362 assert_eq!(1, entry.len());
3363 let mut rel = entry.entries().next().unwrap();
3364 rel.remove();
3365 assert!(entry.is_empty());
3366 assert_eq!(0, entry.len());
3367 }
3368
3369 #[test]
3370 fn test_entry_is_empty() {
3371 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3372 .parse()
3373 .unwrap();
3374 assert!(!entry.is_empty());
3375 assert_eq!(2, entry.len());
3376 let mut rel = entry.relations().next().unwrap();
3377 rel.remove();
3378 assert!(!entry.is_empty());
3379 assert_eq!(1, entry.len());
3380 let mut rel = entry.relations().next().unwrap();
3381 rel.remove();
3382 assert!(entry.is_empty());
3383 assert_eq!(0, entry.len());
3384 }
3385
3386 #[test]
3387 fn test_relation_set_version() {
3388 let mut rel: Relation = "samba".parse().unwrap();
3389 rel.set_version(None);
3390 assert_eq!("samba", rel.to_string());
3391 rel.set_version(Some((
3392 VersionConstraint::GreaterThanEqual,
3393 "2.0".parse().unwrap(),
3394 )));
3395 assert_eq!("samba (>= 2.0)", rel.to_string());
3396 rel.set_version(None);
3397 assert_eq!("samba", rel.to_string());
3398 rel.set_version(Some((
3399 VersionConstraint::GreaterThanEqual,
3400 "2.0".parse().unwrap(),
3401 )));
3402 rel.set_version(Some((
3403 VersionConstraint::GreaterThanEqual,
3404 "1.1".parse().unwrap(),
3405 )));
3406 assert_eq!("samba (>= 1.1)", rel.to_string());
3407 }
3408
3409 #[test]
3410 fn test_replace_relation() {
3411 let mut entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
3412 .parse()
3413 .unwrap();
3414 let new_rel = Relation::simple("python3-breezy");
3415 entry.replace(0, new_rel);
3416 assert_eq!(
3417 entry.to_string(),
3418 "python3-breezy | python3-dulwich (<< 0.18)"
3419 );
3420 }
3421
3422 #[test]
3423 fn test_entry_push_relation() {
3424 let relations: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
3425 let new_rel = Relation::simple("python3-breezy");
3426 let mut entry = relations.entries().next().unwrap();
3427 entry.push(new_rel);
3428 assert_eq!(
3429 entry.to_string(),
3430 "python3-dulwich (>= 0.20.21) | python3-breezy"
3431 );
3432 assert_eq!(
3433 relations.to_string(),
3434 "python3-dulwich (>= 0.20.21) | python3-breezy"
3435 );
3436 }
3437
3438 #[test]
3439 fn test_relations_remove_empty_entry() {
3440 let (mut relations, errors) = Relations::parse_relaxed("foo, , bar, ", false);
3441 assert_eq!(errors, Vec::<String>::new());
3442 assert_eq!(relations.to_string(), "foo, , bar, ");
3443 assert_eq!(relations.len(), 2);
3444 assert_eq!(
3445 relations.entries().next().unwrap().to_string(),
3446 "foo".to_string()
3447 );
3448 assert_eq!(
3449 relations.entries().nth(1).unwrap().to_string(),
3450 "bar".to_string()
3451 );
3452 relations.remove_entry(1);
3453 assert_eq!(relations.to_string(), "foo, , ");
3454 }
3455
3456 #[test]
3457 fn test_entry_remove_relation() {
3458 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
3459 let removed = entry.remove_relation(0);
3460 assert_eq!(removed.to_string(), "python3-dulwich");
3461 assert_eq!(entry.to_string(), "samba");
3462 }
3463
3464 #[test]
3465 fn test_wrap_and_sort_removes_empty_entries() {
3466 let relations: Relations = "foo, , bar, ".parse().unwrap();
3467 let wrapped = relations.wrap_and_sort();
3468 assert_eq!(wrapped.to_string(), "bar, foo");
3469 }
3470
3471 #[test]
3472 fn test_set_archqual() {
3473 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
3474 let mut rel = entry.relations().next().unwrap();
3475 rel.set_archqual("amd64");
3476 assert_eq!(rel.to_string(), "python3-dulwich:amd64");
3477 assert_eq!(rel.archqual(), Some("amd64".to_string()));
3478 assert_eq!(entry.to_string(), "python3-dulwich:amd64 | samba");
3479 rel.set_archqual("i386");
3480 assert_eq!(rel.to_string(), "python3-dulwich:i386");
3481 assert_eq!(rel.archqual(), Some("i386".to_string()));
3482 assert_eq!(entry.to_string(), "python3-dulwich:i386 | samba");
3483 }
3484
3485 #[test]
3486 fn test_set_architectures() {
3487 let mut relation = Relation::simple("samba");
3488 relation.set_architectures(vec!["amd64", "i386"].into_iter());
3489 assert_eq!(relation.to_string(), "samba [amd64 i386]");
3490 }
3491
3492 #[test]
3493 fn test_relation_builder_no_architectures() {
3494 let relation = Relation::build("debhelper").build();
3496 assert_eq!(relation.to_string(), "debhelper");
3497 }
3498
3499 #[test]
3500 fn test_relation_builder_with_architectures() {
3501 let relation = Relation::build("samba")
3503 .architectures(vec!["amd64".to_string(), "i386".to_string()])
3504 .build();
3505 assert_eq!(relation.to_string(), "samba [amd64 i386]");
3506 }
3507
3508 #[test]
3509 fn test_ensure_minimum_version_add_new() {
3510 let mut relations: Relations = "python3".parse().unwrap();
3511 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3512 assert_eq!(relations.to_string(), "python3, debhelper (>= 12)");
3513 }
3514
3515 #[test]
3516 fn test_ensure_minimum_version_update() {
3517 let mut relations: Relations = "debhelper (>= 9)".parse().unwrap();
3518 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3519 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3520 }
3521
3522 #[test]
3523 fn test_ensure_minimum_version_no_change() {
3524 let mut relations: Relations = "debhelper (>= 13)".parse().unwrap();
3525 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3526 assert_eq!(relations.to_string(), "debhelper (>= 13)");
3527 }
3528
3529 #[test]
3530 fn test_ensure_minimum_version_no_version() {
3531 let mut relations: Relations = "debhelper".parse().unwrap();
3532 relations.ensure_minimum_version("debhelper", &"12".parse().unwrap());
3533 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3534 }
3535
3536 #[test]
3537 fn test_ensure_exact_version_add_new() {
3538 let mut relations: Relations = "python3".parse().unwrap();
3539 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3540 assert_eq!(relations.to_string(), "python3, debhelper (= 12)");
3541 }
3542
3543 #[test]
3544 fn test_ensure_exact_version_update() {
3545 let mut relations: Relations = "debhelper (>= 9)".parse().unwrap();
3546 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3547 assert_eq!(relations.to_string(), "debhelper (= 12)");
3548 }
3549
3550 #[test]
3551 fn test_ensure_exact_version_no_change() {
3552 let mut relations: Relations = "debhelper (= 12)".parse().unwrap();
3553 relations.ensure_exact_version("debhelper", &"12".parse().unwrap());
3554 assert_eq!(relations.to_string(), "debhelper (= 12)");
3555 }
3556
3557 #[test]
3558 fn test_ensure_some_version_add_new() {
3559 let mut relations: Relations = "python3".parse().unwrap();
3560 relations.ensure_some_version("debhelper");
3561 assert_eq!(relations.to_string(), "python3, debhelper");
3562 }
3563
3564 #[test]
3565 fn test_ensure_some_version_exists_with_version() {
3566 let mut relations: Relations = "debhelper (>= 12)".parse().unwrap();
3567 relations.ensure_some_version("debhelper");
3568 assert_eq!(relations.to_string(), "debhelper (>= 12)");
3569 }
3570
3571 #[test]
3572 fn test_ensure_some_version_exists_no_version() {
3573 let mut relations: Relations = "debhelper".parse().unwrap();
3574 relations.ensure_some_version("debhelper");
3575 assert_eq!(relations.to_string(), "debhelper");
3576 }
3577
3578 #[test]
3579 fn test_ensure_substvar() {
3580 let mut relations: Relations = "python3".parse().unwrap();
3581 relations.ensure_substvar("${misc:Depends}").unwrap();
3582 assert_eq!(relations.to_string(), "python3, ${misc:Depends}");
3583 }
3584
3585 #[test]
3586 fn test_ensure_substvar_already_exists() {
3587 let (mut relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}", true);
3588 relations.ensure_substvar("${misc:Depends}").unwrap();
3589 assert_eq!(relations.to_string(), "python3, ${misc:Depends}");
3590 }
3591
3592 #[test]
3593 fn test_ensure_substvar_empty_relations() {
3594 let mut relations: Relations = Relations::new();
3595 relations.ensure_substvar("${misc:Depends}").unwrap();
3596 assert_eq!(relations.to_string(), "${misc:Depends}");
3597 }
3598
3599 #[test]
3600 fn test_ensure_substvar_preserves_whitespace() {
3601 let (mut relations, _) = Relations::parse_relaxed("python3, rustc", false);
3603 relations.ensure_substvar("${misc:Depends}").unwrap();
3604 assert_eq!(relations.to_string(), "python3, rustc, ${misc:Depends}");
3606 }
3607
3608 #[test]
3609 fn test_filter_entries_basic() {
3610 let mut relations: Relations = "python3, debhelper, rustc".parse().unwrap();
3611 relations.filter_entries(|entry| entry.relations().any(|r| r.name().starts_with("python")));
3612 assert_eq!(relations.to_string(), "python3");
3613 }
3614
3615 #[test]
3616 fn test_filter_entries_keep_all() {
3617 let mut relations: Relations = "python3, debhelper".parse().unwrap();
3618 relations.filter_entries(|_| true);
3619 assert_eq!(relations.to_string(), "python3, debhelper");
3620 }
3621
3622 #[test]
3623 fn test_filter_entries_remove_all() {
3624 let mut relations: Relations = "python3, debhelper".parse().unwrap();
3625 relations.filter_entries(|_| false);
3626 assert_eq!(relations.to_string(), "");
3627 }
3628
3629 #[test]
3630 fn test_filter_entries_keep_middle() {
3631 let mut relations: Relations = "aaa, bbb, ccc".parse().unwrap();
3632 relations.filter_entries(|entry| entry.relations().any(|r| r.name() == "bbb"));
3633 assert_eq!(relations.to_string(), "bbb");
3634 }
3635
3636 #[test]
3639 fn test_is_sorted_wrap_and_sort_order() {
3640 let relations: Relations = "debhelper, python3, rustc".parse().unwrap();
3642 assert!(relations.is_sorted(&WrapAndSortOrder));
3643
3644 let relations: Relations = "rustc, debhelper, python3".parse().unwrap();
3646 assert!(!relations.is_sorted(&WrapAndSortOrder));
3647
3648 let (relations, _) =
3650 Relations::parse_relaxed("cdbs, debhelper-compat, python3, ${misc:Depends}", true);
3651 assert!(relations.is_sorted(&WrapAndSortOrder));
3652 }
3653
3654 #[test]
3655 fn test_is_sorted_default_order() {
3656 let relations: Relations = "aaa, bbb, ccc".parse().unwrap();
3658 assert!(relations.is_sorted(&DefaultSortingOrder));
3659
3660 let relations: Relations = "ccc, aaa, bbb".parse().unwrap();
3662 assert!(!relations.is_sorted(&DefaultSortingOrder));
3663
3664 let (relations, _) = Relations::parse_relaxed("aaa, bbb, ${misc:Depends}", true);
3666 assert!(relations.is_sorted(&DefaultSortingOrder));
3667 }
3668
3669 #[test]
3670 fn test_is_sorted_with_substvars() {
3671 let (relations, _) = Relations::parse_relaxed("python3, ${misc:Depends}, rustc", true);
3673 assert!(relations.is_sorted(&DefaultSortingOrder));
3675 }
3676
3677 #[test]
3678 fn test_drop_dependency_exists() {
3679 let mut relations: Relations = "python3, debhelper, rustc".parse().unwrap();
3680 assert!(relations.drop_dependency("debhelper"));
3681 assert_eq!(relations.to_string(), "python3, rustc");
3682 }
3683
3684 #[test]
3685 fn test_drop_dependency_not_exists() {
3686 let mut relations: Relations = "python3, rustc".parse().unwrap();
3687 assert!(!relations.drop_dependency("nonexistent"));
3688 assert_eq!(relations.to_string(), "python3, rustc");
3689 }
3690
3691 #[test]
3692 fn test_drop_dependency_only_item() {
3693 let mut relations: Relations = "python3".parse().unwrap();
3694 assert!(relations.drop_dependency("python3"));
3695 assert_eq!(relations.to_string(), "");
3696 }
3697
3698 #[test]
3699 fn test_add_dependency_to_empty() {
3700 let mut relations: Relations = "".parse().unwrap();
3701 let entry = Entry::from(Relation::simple("python3"));
3702 relations.add_dependency(entry, None);
3703 assert_eq!(relations.to_string(), "python3");
3704 }
3705
3706 #[test]
3707 fn test_add_dependency_sorted_position() {
3708 let mut relations: Relations = "debhelper, rustc".parse().unwrap();
3709 let entry = Entry::from(Relation::simple("python3"));
3710 relations.add_dependency(entry, None);
3711 assert_eq!(relations.to_string(), "debhelper, python3, rustc");
3713 }
3714
3715 #[test]
3716 fn test_add_dependency_explicit_position() {
3717 let mut relations: Relations = "python3, rustc".parse().unwrap();
3718 let entry = Entry::from(Relation::simple("debhelper"));
3719 relations.add_dependency(entry, Some(0));
3720 assert_eq!(relations.to_string(), "debhelper, python3, rustc");
3721 }
3722
3723 #[test]
3724 fn test_add_dependency_build_system_first() {
3725 let mut relations: Relations = "python3, rustc".parse().unwrap();
3726 let entry = Entry::from(Relation::simple("debhelper-compat"));
3727 relations.add_dependency(entry, None);
3728 assert_eq!(relations.to_string(), "debhelper-compat, python3, rustc");
3730 }
3731
3732 #[test]
3733 fn test_add_dependency_at_end() {
3734 let mut relations: Relations = "debhelper, python3".parse().unwrap();
3735 let entry = Entry::from(Relation::simple("zzz-package"));
3736 relations.add_dependency(entry, None);
3737 assert_eq!(relations.to_string(), "debhelper, python3, zzz-package");
3739 }
3740
3741 #[test]
3742 fn test_get_relation_exists() {
3743 let relations: Relations = "python3, debhelper (>= 12), rustc".parse().unwrap();
3744 let result = relations.get_relation("debhelper");
3745 assert!(result.is_ok());
3746 let (idx, entry) = result.unwrap();
3747 assert_eq!(idx, 1);
3748 assert_eq!(entry.to_string(), "debhelper (>= 12)");
3749 }
3750
3751 #[test]
3752 fn test_get_relation_not_exists() {
3753 let relations: Relations = "python3, rustc".parse().unwrap();
3754 let result = relations.get_relation("nonexistent");
3755 assert_eq!(result, Err("Package nonexistent not found".to_string()));
3756 }
3757
3758 #[test]
3759 fn test_get_relation_complex_rule() {
3760 let relations: Relations = "python3 | python3-minimal, rustc".parse().unwrap();
3761 let result = relations.get_relation("python3");
3762 assert_eq!(
3763 result,
3764 Err("Complex rule for python3, aborting".to_string())
3765 );
3766 }
3767
3768 #[test]
3769 fn test_iter_relations_for_simple() {
3770 let relations: Relations = "python3, debhelper, python3-dev".parse().unwrap();
3771 let entries: Vec<_> = relations.iter_relations_for("python3").collect();
3772 assert_eq!(entries.len(), 1);
3773 assert_eq!(entries[0].0, 0);
3774 assert_eq!(entries[0].1.to_string(), "python3");
3775 }
3776
3777 #[test]
3778 fn test_iter_relations_for_alternatives() {
3779 let relations: Relations = "python3 | python3-minimal, python3-dev".parse().unwrap();
3780 let entries: Vec<_> = relations.iter_relations_for("python3").collect();
3781 assert_eq!(entries.len(), 1);
3783 assert_eq!(entries[0].0, 0);
3784 }
3785
3786 #[test]
3787 fn test_iter_relations_for_not_found() {
3788 let relations: Relations = "python3, rustc".parse().unwrap();
3789 let entries: Vec<_> = relations.iter_relations_for("debhelper").collect();
3790 assert_eq!(entries.len(), 0);
3791 }
3792
3793 #[test]
3794 fn test_has_relation_exists() {
3795 let relations: Relations = "python3, debhelper, rustc".parse().unwrap();
3796 assert!(relations.has_relation("debhelper"));
3797 assert!(relations.has_relation("python3"));
3798 assert!(relations.has_relation("rustc"));
3799 }
3800
3801 #[test]
3802 fn test_has_relation_not_exists() {
3803 let relations: Relations = "python3, rustc".parse().unwrap();
3804 assert!(!relations.has_relation("debhelper"));
3805 }
3806
3807 #[test]
3808 fn test_has_relation_in_alternative() {
3809 let relations: Relations = "python3 | python3-minimal".parse().unwrap();
3810 assert!(relations.has_relation("python3"));
3811 assert!(relations.has_relation("python3-minimal"));
3812 }
3813
3814 #[test]
3815 fn test_sorting_order_wrap_and_sort_build_systems() {
3816 let order = WrapAndSortOrder;
3817 assert!(order.lt("debhelper", "python3"));
3819 assert!(order.lt("debhelper-compat", "rustc"));
3820 assert!(order.lt("cdbs", "aaa"));
3821 assert!(order.lt("dh-python", "python3"));
3822 }
3823
3824 #[test]
3825 fn test_sorting_order_wrap_and_sort_regular_packages() {
3826 let order = WrapAndSortOrder;
3827 assert!(order.lt("aaa", "bbb"));
3829 assert!(order.lt("python3", "rustc"));
3830 assert!(!order.lt("rustc", "python3"));
3831 }
3832
3833 #[test]
3834 fn test_sorting_order_wrap_and_sort_substvars() {
3835 let order = WrapAndSortOrder;
3836 assert!(order.lt("python3", "${misc:Depends}"));
3838 assert!(!order.lt("${misc:Depends}", "python3"));
3839 assert!(!order.ignore("${misc:Depends}"));
3841 }
3842
3843 #[test]
3844 fn test_sorting_order_default_special_items() {
3845 let order = DefaultSortingOrder;
3846 assert!(order.lt("python3", "${misc:Depends}"));
3848 assert!(order.lt("aaa", "@cdbs@"));
3849 assert!(order.ignore("${misc:Depends}"));
3851 assert!(order.ignore("@cdbs@"));
3852 assert!(!order.ignore("python3"));
3853 }
3854
3855 #[test]
3856 fn test_is_special_package_name() {
3857 assert!(is_special_package_name("${misc:Depends}"));
3858 assert!(is_special_package_name("${shlibs:Depends}"));
3859 assert!(is_special_package_name("@cdbs@"));
3860 assert!(!is_special_package_name("python3"));
3861 assert!(!is_special_package_name("debhelper"));
3862 }
3863
3864 #[test]
3865 fn test_add_dependency_with_explicit_position() {
3866 let mut relations: Relations = "python3, rustc".parse().unwrap();
3868 let entry = Entry::from(Relation::simple("debhelper"));
3869 relations.add_dependency(entry, Some(1));
3870 assert_eq!(relations.to_string(), "python3, debhelper, rustc");
3872 }
3873
3874 #[test]
3875 fn test_whitespace_detection_single_space() {
3876 let mut relations: Relations = "python3, rustc".parse().unwrap();
3877 let entry = Entry::from(Relation::simple("debhelper"));
3878 relations.add_dependency(entry, Some(1));
3879 assert_eq!(relations.to_string(), "python3, debhelper, rustc");
3880 }
3881
3882 #[test]
3883 fn test_whitespace_detection_multiple_spaces() {
3884 let mut relations: Relations = "python3, rustc, gcc".parse().unwrap();
3885 let entry = Entry::from(Relation::simple("debhelper"));
3886 relations.add_dependency(entry, Some(1));
3887 assert_eq!(relations.to_string(), "python3, debhelper, rustc, gcc");
3889 }
3890
3891 #[test]
3892 fn test_whitespace_detection_mixed_patterns() {
3893 let mut relations: Relations = "a, b, c, d, e".parse().unwrap();
3895 let entry = Entry::from(Relation::simple("x"));
3896 relations.push(entry);
3897 assert_eq!(relations.to_string(), "a, b, c, d, e, x");
3900 }
3901
3902 #[test]
3903 fn test_whitespace_detection_newlines() {
3904 let mut relations: Relations = "python3,\n rustc".parse().unwrap();
3905 let entry = Entry::from(Relation::simple("debhelper"));
3906 relations.add_dependency(entry, Some(1));
3907 assert_eq!(relations.to_string(), "python3,\n debhelper,\n rustc");
3909 }
3910
3911 #[test]
3912 fn test_append_with_newline_no_trailing() {
3913 let mut relations: Relations = "foo,\n bar".parse().unwrap();
3914 let entry = Entry::from(Relation::simple("blah"));
3915 relations.add_dependency(entry, None);
3916 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
3917 }
3918
3919 #[test]
3920 fn test_append_with_trailing_newline() {
3921 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
3922 let entry = Entry::from(Relation::simple("blah"));
3923 relations.add_dependency(entry, None);
3924 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
3925 }
3926
3927 #[test]
3928 fn test_append_with_4_space_indent() {
3929 let mut relations: Relations = "foo,\n bar".parse().unwrap();
3930 let entry = Entry::from(Relation::simple("blah"));
3931 relations.add_dependency(entry, None);
3932 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
3933 }
3934
3935 #[test]
3936 fn test_append_with_4_space_and_trailing_newline() {
3937 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
3938 let entry = Entry::from(Relation::simple("blah"));
3939 relations.add_dependency(entry, None);
3940 assert_eq!(relations.to_string(), "foo,\n bar,\n blah");
3941 }
3942
3943 #[test]
3944 fn test_odd_syntax_append_no_trailing() {
3945 let mut relations: Relations = "\n foo\n , bar".parse().unwrap();
3946 let entry = Entry::from(Relation::simple("blah"));
3947 relations.add_dependency(entry, None);
3948 assert_eq!(relations.to_string(), "\n foo\n , bar\n , blah");
3949 }
3950
3951 #[test]
3952 fn test_odd_syntax_append_with_trailing() {
3953 let mut relations: Relations = "\n foo\n , bar\n".parse().unwrap();
3954 let entry = Entry::from(Relation::simple("blah"));
3955 relations.add_dependency(entry, None);
3956 assert_eq!(relations.to_string(), "\n foo\n , bar\n , blah");
3957 }
3958
3959 #[test]
3960 fn test_insert_at_1_no_trailing() {
3961 let mut relations: Relations = "foo,\n bar".parse().unwrap();
3962 let entry = Entry::from(Relation::simple("blah"));
3963 relations.add_dependency(entry, Some(1));
3964 assert_eq!(relations.to_string(), "foo,\n blah,\n bar");
3965 }
3966
3967 #[test]
3968 fn test_insert_at_1_with_trailing() {
3969 let mut relations: Relations = "foo,\n bar\n".parse().unwrap();
3970 let entry = Entry::from(Relation::simple("blah"));
3971 relations.add_dependency(entry, Some(1));
3972 assert_eq!(relations.to_string(), "foo,\n blah,\n bar");
3973 }
3974
3975 #[test]
3976 fn test_odd_syntax_insert_at_1() {
3977 let mut relations: Relations = "\n foo\n , bar\n".parse().unwrap();
3978 let entry = Entry::from(Relation::simple("blah"));
3979 relations.add_dependency(entry, Some(1));
3980 assert_eq!(relations.to_string(), "\n foo\n , blah\n , bar");
3981 }
3982}