1use crate::RCode;
13use deb822_lossless::Paragraph;
14pub use relations::{Relation, Relations, Version};
15
16pub struct RDescription(Paragraph);
18
19impl std::fmt::Display for RDescription {
20 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
21 write!(f, "{}", self.0)
22 }
23}
24
25impl Default for RDescription {
26 fn default() -> Self {
27 Self(Paragraph::new())
28 }
29}
30
31impl From<RDescription> for Paragraph {
32 fn from(description: RDescription) -> Self {
33 description.0
34 }
35}
36
37impl From<Paragraph> for RDescription {
38 fn from(paragraph: Paragraph) -> Self {
39 Self(paragraph)
40 }
41}
42
43#[derive(Debug)]
44pub enum Error {
46 Io(std::io::Error),
48
49 Parse(deb822_lossless::ParseError),
51}
52
53impl std::fmt::Display for Error {
54 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
55 match self {
56 Self::Io(e) => write!(f, "IO error: {e}"),
57 Self::Parse(e) => write!(f, "Parse error: {e}"),
58 }
59 }
60}
61
62impl std::error::Error for Error {}
63
64impl From<deb822_lossless::ParseError> for Error {
65 fn from(e: deb822_lossless::ParseError) -> Self {
66 Self::Parse(e)
67 }
68}
69
70impl From<std::io::Error> for Error {
71 fn from(e: std::io::Error) -> Self {
72 Self::Io(e)
73 }
74}
75
76impl std::str::FromStr for RDescription {
77 type Err = Error;
78
79 fn from_str(s: &str) -> Result<Self, Self::Err> {
80 Ok(Self(Paragraph::from_str(s)?))
81 }
82}
83
84impl RDescription {
85 pub fn new() -> Self {
87 Self(Paragraph::new())
88 }
89
90 pub fn package(&self) -> Option<String> {
92 self.0.get("Package")
93 }
94
95 pub fn set_package(&mut self, package: &str) {
97 self.0.set("Package", package);
98 }
99
100 pub fn title(&self) -> Option<String> {
105 self.0.get("Title")
106 }
107
108 pub fn maintainer(&self) -> Option<String> {
110 self.0.get("Maintainer")
111 }
112
113 pub fn set_maintainer(&mut self, maintainer: &str) {
115 self.0.set("Maintainer", maintainer);
116 }
117
118 pub fn authors(&self) -> Option<RCode> {
120 self.0.get("Authors@R").map(|s| s.parse().unwrap())
121 }
122
123 pub fn set_authors(&mut self, authors: &RCode) {
125 self.0.set("Authors@R", &authors.to_string());
126 }
127
128 pub fn set_title(&mut self, title: &str) {
130 self.0.set("Title", title);
131 }
132
133 pub fn description(&self) -> Option<String> {
135 self.0.get("Description")
136 }
137
138 pub fn set_description(&mut self, description: &str) {
140 self.0.set("Description", description);
141 }
142
143 pub fn version(&self) -> Option<String> {
145 self.0.get("Version")
146 }
147
148 pub fn set_version(&mut self, version: &str) {
150 self.0.set("Version", version);
151 }
152
153 pub fn encoding(&self) -> Option<String> {
155 self.0.get("Encoding")
156 }
157
158 pub fn set_encoding(&mut self, encoding: &str) {
160 self.0.set("Encoding", encoding);
161 }
162
163 pub fn license(&self) -> Option<String> {
165 self.0.get("License")
166 }
167
168 pub fn set_license(&mut self, license: &str) {
170 self.0.set("License", license);
171 }
172
173 pub fn roxygen_note(&self) -> Option<String> {
175 self.0.get("RoxygenNote")
176 }
177
178 pub fn set_roxygen_note(&mut self, roxygen_note: &str) {
180 self.0.set("RoxygenNote", roxygen_note);
181 }
182
183 pub fn roxygen(&self) -> Option<String> {
185 self.0.get("Roxygen")
186 }
187
188 pub fn set_roxygen(&mut self, roxygen: &str) {
190 self.0.set("Roxygen", roxygen);
191 }
192
193 pub fn url(&self) -> Option<String> {
195 self.0.get("URL")
197 }
198
199 pub fn set_url(&mut self, url: &str) {
201 self.0.set("URL", url);
203 }
204
205 pub fn bug_reports(&self) -> Option<url::Url> {
207 self.0
208 .get("BugReports")
209 .map(|s| url::Url::parse(s.as_str()).unwrap())
210 }
211
212 pub fn set_bug_reports(&mut self, bug_reports: &url::Url) {
214 self.0.set("BugReports", bug_reports.as_str());
215 }
216
217 pub fn imports(&self) -> Option<Relations> {
219 self.0.get("Imports").map(|s| s.parse().unwrap())
220 }
221
222 pub fn set_imports(&mut self, imports: Relations) {
224 self.0.set("Imports", &imports.to_string());
225 }
226
227 pub fn suggests(&self) -> Option<Relations> {
229 self.0.get("Suggests").map(|s| s.parse().unwrap())
230 }
231
232 pub fn set_suggests(&mut self, suggests: Relations) {
234 self.0.set("Suggests", &suggests.to_string());
235 }
236
237 pub fn depends(&self) -> Option<Relations> {
239 self.0.get("Depends").map(|s| s.parse().unwrap())
240 }
241
242 pub fn set_depends(&mut self, depends: Relations) {
244 self.0.set("Depends", &depends.to_string());
245 }
246
247 pub fn linking_to(&self) -> Option<Relations> {
249 self.0.get("LinkingTo").map(|s| s.parse().unwrap())
250 }
251
252 pub fn set_linking_to(&mut self, linking_to: Relations) {
254 self.0.set("LinkingTo", &linking_to.to_string());
255 }
256
257 pub fn enhances(&self) -> Option<Relations> {
259 self.0.get("Enhances").map(|s| s.parse().unwrap())
260 }
261
262 pub fn set_enhances(&mut self, enhances: Relations) {
264 self.0.set("Enhances", &enhances.to_string());
265 }
266
267 pub fn lazy_data(&self) -> Option<bool> {
269 self.0.get("LazyData").map(|s| s == "true")
270 }
271
272 pub fn set_lazy_data(&mut self, lazy_data: bool) {
274 self.0
275 .set("LazyData", if lazy_data { "true" } else { "false" });
276 }
277
278 pub fn collate(&self) -> Option<String> {
280 self.0.get("Collate")
281 }
282
283 pub fn set_collate(&mut self, collate: &str) {
285 self.0.set("Collate", collate);
286 }
287
288 pub fn vignette_builder(&self) -> Option<Vec<String>> {
290 self.0
291 .get("VignetteBuilder")
292 .map(|s| s.split(',').map(|s| s.trim().to_string()).collect())
293 }
294
295 pub fn set_vignette_builder(&mut self, vignette_builder: &[&str]) {
297 self.0.set("VignetteBuilder", &vignette_builder.join(", "));
298 }
299
300 pub fn system_requirements(&self) -> Option<Vec<String>> {
302 self.0
303 .get("SystemRequirements")
304 .map(|s| s.split(',').map(|s| s.trim().to_string()).collect())
305 }
306
307 pub fn set_system_requirements(&mut self, system_requirements: &[&str]) {
309 self.0
310 .set("SystemRequirements", &system_requirements.join(", "));
311 }
312
313 pub fn date(&self) -> Option<String> {
315 self.0.get("Date")
316 }
317
318 pub fn set_date(&mut self, date: &str) {
320 self.0.set("Date", date);
321 }
322
323 pub fn repository(&self) -> Option<String> {
327 self.0.get("Repository")
328 }
329
330 pub fn set_repository(&mut self, repository: &str) {
332 self.0.set("Repository", repository);
333 }
334
335 pub fn additional_repositories(&self) -> Option<Vec<String>> {
337 self.0.get("Additional_repositories").map(|s| {
338 s.split(',')
339 .map(|s| s.trim().to_string())
340 .filter(|s| !s.is_empty())
341 .collect()
342 })
343 }
344
345 pub fn set_additional_repositories(&mut self, repositories: &[&str]) {
347 self.0
348 .set("Additional_repositories", &repositories.join(", "));
349 }
350}
351
352pub mod relations {
353 use crate::relations::SyntaxKind::{self, *};
372 use crate::relations::VersionConstraint;
373 use crate::version::Version as LossyVersion;
374 use rowan::{Direction, NodeOrToken};
375 use std::cmp::Ordering;
376 use std::hash::{Hash, Hasher};
377
378 #[derive(Debug, Clone)]
379 pub struct Version {
384 pub components: Vec<u32>,
386 original: String,
387 }
388
389 impl Version {
390 pub fn as_str(&self) -> &str {
392 &self.original
393 }
394 }
395
396 impl std::fmt::Display for Version {
397 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
398 f.write_str(&self.original)
399 }
400 }
401
402 impl std::str::FromStr for Version {
403 type Err = String;
404
405 fn from_str(s: &str) -> Result<Self, Self::Err> {
406 let components = s
407 .split(|c| c == '.' || c == '-')
408 .map(|part| {
409 part.parse()
410 .map_err(|_| format!("Invalid version component: {s}"))
411 })
412 .collect::<Result<Vec<_>, _>>()?;
413
414 if components.len() < 2 {
415 return Err(format!("Invalid version string: {s}"));
416 }
417
418 Ok(Self {
419 components,
420 original: s.to_string(),
421 })
422 }
423 }
424
425 impl From<Version> for LossyVersion {
426 fn from(version: Version) -> Self {
427 Self {
428 components: version.components,
429 }
430 }
431 }
432
433 impl From<LossyVersion> for Version {
434 fn from(version: LossyVersion) -> Self {
435 let original = version.to_string();
436 Self {
437 components: version.components,
438 original,
439 }
440 }
441 }
442
443 impl PartialEq for Version {
444 fn eq(&self, other: &Self) -> bool {
445 self.components == other.components
446 }
447 }
448
449 impl Eq for Version {}
450
451 impl Hash for Version {
452 fn hash<H: Hasher>(&self, state: &mut H) {
453 self.components.hash(state);
454 }
455 }
456
457 impl Ord for Version {
458 fn cmp(&self, other: &Self) -> Ordering {
459 compare_components(&self.components, &other.components)
460 }
461 }
462
463 impl PartialOrd for Version {
464 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
465 Some(self.cmp(other))
466 }
467 }
468
469 fn compare_components(a_components: &[u32], b_components: &[u32]) -> Ordering {
470 for (a, b) in a_components.iter().zip(b_components.iter()) {
471 match a.cmp(b) {
472 Ordering::Equal => continue,
473 ordering => return ordering,
474 }
475 }
476 a_components.len().cmp(&b_components.len())
477 }
478
479 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
481 pub struct ParseError(Vec<String>);
482
483 impl std::fmt::Display for ParseError {
484 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
485 for err in &self.0 {
486 writeln!(f, "{err}")?;
487 }
488 Ok(())
489 }
490 }
491
492 impl std::error::Error for ParseError {}
493
494 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
498 enum Lang {}
499 impl rowan::Language for Lang {
500 type Kind = SyntaxKind;
501 fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
502 unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
503 }
504 fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
505 kind.into()
506 }
507 }
508
509 use rowan::{GreenNode, GreenToken};
512
513 use rowan::GreenNodeBuilder;
517
518 fn push_constraint_tokens(builder: &mut GreenNodeBuilder, vc: &VersionConstraint) {
522 for c in vc.to_string().chars() {
523 let kind = match c {
524 '>' => R_ANGLE,
525 '<' => L_ANGLE,
526 '=' => EQUAL,
527 '!' => NOT,
528 _ => unreachable!("unexpected constraint character {c:?}"),
529 };
530 builder.token(kind.into(), c.to_string().as_str());
531 }
532 }
533
534 struct Parse {
537 green_node: GreenNode,
538 #[allow(unused)]
539 errors: Vec<String>,
540 }
541
542 fn parse(text: &str) -> Parse {
543 struct Parser {
544 tokens: Vec<(SyntaxKind, String)>,
547 builder: GreenNodeBuilder<'static>,
549 errors: Vec<String>,
552 }
553
554 impl Parser {
555 fn error(&mut self, error: String) {
556 self.errors.push(error);
557 self.builder.start_node(SyntaxKind::ERROR.into());
558 if self.current().is_some() {
559 self.bump();
560 }
561 self.builder.finish_node();
562 }
563
564 fn parse_relation(&mut self) {
565 self.builder.start_node(SyntaxKind::RELATION.into());
566 if self.current() == Some(IDENT) {
567 self.bump();
568 } else {
569 self.error("Expected package name".to_string());
570 }
571 match self.peek_past_ws() {
572 Some(COMMA) => {}
573 None | Some(L_PARENS) => {
574 self.skip_ws();
575 }
576 e => {
577 self.skip_ws();
578 self.error(format!(
579 "Expected ':' or '|' or '[' or '<' or ',' but got {e:?}"
580 ));
581 }
582 }
583
584 if self.peek_past_ws() == Some(L_PARENS) {
585 self.skip_ws();
586 self.builder.start_node(VERSION.into());
587 self.bump();
588 self.skip_ws();
589
590 self.builder.start_node(CONSTRAINT.into());
591
592 while self.current() == Some(L_ANGLE)
593 || self.current() == Some(R_ANGLE)
594 || self.current() == Some(EQUAL)
595 || self.current() == Some(NOT)
596 {
597 self.bump();
598 }
599
600 self.builder.finish_node();
601
602 self.skip_ws();
603
604 if self.current() == Some(IDENT) {
605 self.bump();
606 } else {
607 self.error("Expected version".to_string());
608 }
609
610 if self.current() == Some(R_PARENS) {
611 self.bump();
612 } else {
613 self.error("Expected ')'".to_string());
614 }
615
616 self.builder.finish_node();
617 }
618
619 self.builder.finish_node();
620 }
621
622 fn parse(mut self) -> Parse {
623 self.builder.start_node(SyntaxKind::ROOT.into());
624
625 self.skip_ws();
626
627 while self.current().is_some() {
628 match self.current() {
629 Some(IDENT) => self.parse_relation(),
630 Some(COMMA) => {
631 }
633 Some(c) => {
634 self.error(format!("expected identifier or comma but got {c:?}"));
635 }
636 None => {
637 self.error("expected identifier but got end of file".to_string());
638 }
639 }
640
641 self.skip_ws();
642 match self.current() {
643 Some(COMMA) => {
644 self.bump();
645 }
646 None => {
647 break;
648 }
649 c => {
650 self.error(format!("expected comma or end of file but got {c:?}"));
651 }
652 }
653 self.skip_ws();
654 }
655
656 self.builder.finish_node();
657 Parse {
659 green_node: self.builder.finish(),
660 errors: self.errors,
661 }
662 }
663 fn bump(&mut self) {
665 let (kind, text) = self.tokens.pop().unwrap();
666 self.builder.token(kind.into(), text.as_str());
667 }
668 fn current(&self) -> Option<SyntaxKind> {
670 self.tokens.last().map(|(kind, _)| *kind)
671 }
672 fn skip_ws(&mut self) {
673 while self.current() == Some(WHITESPACE) || self.current() == Some(NEWLINE) {
674 self.bump()
675 }
676 }
677
678 fn peek_past_ws(&self) -> Option<SyntaxKind> {
679 let mut i = self.tokens.len();
680 while i > 0 {
681 i -= 1;
682 match self.tokens[i].0 {
683 WHITESPACE | NEWLINE => {}
684 _ => return Some(self.tokens[i].0),
685 }
686 }
687 None
688 }
689 }
690
691 let mut tokens = crate::relations::lex(text);
692 tokens.reverse();
693 Parser {
694 tokens,
695 builder: GreenNodeBuilder::new(),
696 errors: Vec::new(),
697 }
698 .parse()
699 }
700
701 type SyntaxNode = rowan::SyntaxNode<Lang>;
707 #[allow(unused)]
708 type SyntaxToken = rowan::SyntaxToken<Lang>;
709 #[allow(unused)]
710 type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;
711
712 impl Parse {
713 fn root_mut(&self) -> Relations {
714 Relations::cast(SyntaxNode::new_root_mut(self.green_node.clone())).unwrap()
715 }
716 }
717
718 macro_rules! ast_node {
719 ($ast:ident, $kind:ident) => {
720 #[repr(transparent)]
722 pub struct $ast(SyntaxNode);
723 impl $ast {
724 #[allow(unused)]
725 fn cast(node: SyntaxNode) -> Option<Self> {
726 if node.kind() == $kind {
727 Some(Self(node))
728 } else {
729 None
730 }
731 }
732 }
733
734 impl std::fmt::Display for $ast {
735 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
736 f.write_str(&self.0.text().to_string())
737 }
738 }
739 };
740 }
741
742 ast_node!(Relations, ROOT);
743 ast_node!(Relation, RELATION);
744
745 impl PartialEq for Relations {
746 fn eq(&self, other: &Self) -> bool {
747 self.relations().collect::<Vec<_>>() == other.relations().collect::<Vec<_>>()
748 }
749 }
750
751 impl PartialEq for Relation {
752 fn eq(&self, other: &Self) -> bool {
753 self.name() == other.name() && self.version() == other.version()
754 }
755 }
756
757 #[cfg(feature = "serde")]
758 impl serde::Serialize for Relations {
759 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
760 let rep = self.to_string();
761 serializer.serialize_str(&rep)
762 }
763 }
764
765 #[cfg(feature = "serde")]
766 impl<'de> serde::Deserialize<'de> for Relations {
767 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
768 let s = String::deserialize(deserializer)?;
769 let relations = s.parse().map_err(serde::de::Error::custom)?;
770 Ok(relations)
771 }
772 }
773
774 impl std::fmt::Debug for Relations {
775 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
776 let mut s = f.debug_struct("Relations");
777
778 for relation in self.relations() {
779 s.field("relation", &relation);
780 }
781
782 s.finish()
783 }
784 }
785
786 impl std::fmt::Debug for Relation {
787 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
788 let mut s = f.debug_struct("Relation");
789
790 s.field("name", &self.name());
791
792 if let Some((vc, version)) = self.version() {
793 s.field("version", &vc);
794 s.field("version", &version);
795 }
796
797 s.finish()
798 }
799 }
800
801 #[cfg(feature = "serde")]
802 impl serde::Serialize for Relation {
803 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
804 let rep = self.to_string();
805 serializer.serialize_str(&rep)
806 }
807 }
808
809 #[cfg(feature = "serde")]
810 impl<'de> serde::Deserialize<'de> for Relation {
811 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
812 let s = String::deserialize(deserializer)?;
813 let relation = s.parse().map_err(serde::de::Error::custom)?;
814 Ok(relation)
815 }
816 }
817
818 impl Default for Relations {
819 fn default() -> Self {
820 Self::new()
821 }
822 }
823
824 impl Relations {
825 pub fn new() -> Self {
827 Self::from(vec![])
828 }
829
830 #[must_use]
832 pub fn wrap_and_sort(self) -> Self {
833 let mut entries = self
834 .relations()
835 .map(|e| e.wrap_and_sort())
836 .collect::<Vec<_>>();
837 entries.sort();
838 Self::from(entries)
840 }
841
842 pub fn relations(&self) -> impl Iterator<Item = Relation> + '_ {
844 self.0.children().filter_map(Relation::cast)
845 }
846
847 pub fn iter(&self) -> impl Iterator<Item = Relation> + '_ {
849 self.relations()
850 }
851
852 pub fn get_relation(&self, idx: usize) -> Option<Relation> {
854 self.relations().nth(idx)
855 }
856
857 pub fn remove_relation(&mut self, idx: usize) -> Relation {
859 let mut relation = self.get_relation(idx).unwrap();
860 relation.remove();
861 relation
862 }
863
864 pub fn insert(&mut self, idx: usize, relation: Relation) {
866 let is_empty = !self.0.children_with_tokens().any(|n| n.kind() == COMMA);
867 let (position, new_children) = if let Some(current_relation) = self.relations().nth(idx)
868 {
869 let to_insert: Vec<NodeOrToken<GreenNode, GreenToken>> = if idx == 0 && is_empty {
870 vec![relation.0.green().into()]
871 } else {
872 vec![
873 relation.0.green().into(),
874 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
875 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
876 ]
877 };
878
879 (current_relation.0.index(), to_insert)
880 } else {
881 let child_count = self.0.children_with_tokens().count();
882 (
883 child_count,
884 if idx == 0 {
885 vec![relation.0.green().into()]
886 } else {
887 vec![
888 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
889 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
890 relation.0.green().into(),
891 ]
892 },
893 )
894 };
895 self.0 = SyntaxNode::new_root_mut(
897 self.0.replace_with(
898 self.0
899 .green()
900 .splice_children(position..position, new_children),
901 ),
902 );
903 }
904
905 pub fn replace(&mut self, idx: usize, relation: Relation) {
907 let current_relation = self.get_relation(idx).unwrap();
908 self.0.splice_children(
909 current_relation.0.index()..current_relation.0.index() + 1,
910 vec![relation.0.into()],
911 );
912 }
913
914 pub fn push(&mut self, relation: Relation) {
916 let pos = self.relations().count();
917 self.insert(pos, relation);
918 }
919
920 pub fn parse_relaxed(s: &str) -> (Relations, Vec<String>) {
922 let parse = parse(s);
923 (parse.root_mut(), parse.errors)
924 }
925
926 pub fn satisfied_by(
928 &self,
929 package_version: impl crate::relations::VersionLookup + Copy,
930 ) -> bool {
931 self.relations().all(|e| e.satisfied_by(package_version))
932 }
933
934 pub fn is_empty(&self) -> bool {
936 self.relations().count() == 0
937 }
938
939 pub fn len(&self) -> usize {
941 self.relations().count()
942 }
943 }
944
945 impl From<Vec<Relation>> for Relations {
946 fn from(entries: Vec<Relation>) -> Self {
947 let mut builder = GreenNodeBuilder::new();
948 builder.start_node(ROOT.into());
949 for (i, relation) in entries.into_iter().enumerate() {
950 if i > 0 {
951 builder.token(COMMA.into(), ",");
952 builder.token(WHITESPACE.into(), " ");
953 }
954 inject(&mut builder, relation.0);
955 }
956 builder.finish_node();
957 Relations(SyntaxNode::new_root_mut(builder.finish()))
958 }
959 }
960
961 impl From<Relation> for Relations {
962 fn from(relation: Relation) -> Self {
963 Self::from(vec![relation])
964 }
965 }
966
967 impl From<Relation> for crate::lossy::Relation {
968 fn from(relation: Relation) -> Self {
969 let mut rel = crate::lossy::Relation::new();
970 rel.name = relation.name();
971 rel.version = relation
972 .version()
973 .map(|(constraint, version)| (constraint, version.into()));
974 rel
975 }
976 }
977
978 impl From<Relations> for crate::lossy::Relations {
979 fn from(relations: Relations) -> Self {
980 let mut rels = crate::lossy::Relations::new();
981 for relation in relations.relations() {
982 rels.0.push(relation.into());
983 }
984 rels
985 }
986 }
987
988 impl From<crate::lossy::Relations> for Relations {
989 fn from(relations: crate::lossy::Relations) -> Self {
990 let mut entries = vec![];
991 for relation in relations.iter() {
992 entries.push(relation.clone().into());
993 }
994 Self::from(entries)
995 }
996 }
997
998 impl From<crate::lossy::Relation> for Relation {
999 fn from(relation: crate::lossy::Relation) -> Self {
1000 Relation::new(
1001 &relation.name,
1002 relation
1003 .version
1004 .map(|(constraint, version)| (constraint, version.into())),
1005 )
1006 }
1007 }
1008
1009 fn inject(builder: &mut GreenNodeBuilder, node: SyntaxNode) {
1010 builder.start_node(node.kind().into());
1011 for child in node.children_with_tokens() {
1012 match child {
1013 rowan::NodeOrToken::Node(child) => {
1014 inject(builder, child);
1015 }
1016 rowan::NodeOrToken::Token(token) => {
1017 builder.token(token.kind().into(), token.text());
1018 }
1019 }
1020 }
1021 builder.finish_node();
1022 }
1023
1024 impl Relation {
1025 pub fn new(name: &str, version_constraint: Option<(VersionConstraint, Version)>) -> Self {
1039 let mut builder = GreenNodeBuilder::new();
1040 builder.start_node(SyntaxKind::RELATION.into());
1041 builder.token(IDENT.into(), name);
1042 if let Some((vc, version)) = version_constraint {
1043 builder.token(WHITESPACE.into(), " ");
1044 builder.start_node(SyntaxKind::VERSION.into());
1045 builder.token(L_PARENS.into(), "(");
1046 builder.start_node(SyntaxKind::CONSTRAINT.into());
1047 push_constraint_tokens(&mut builder, &vc);
1048 builder.finish_node();
1049
1050 builder.token(WHITESPACE.into(), " ");
1051
1052 builder.token(IDENT.into(), version.to_string().as_str());
1053
1054 builder.token(R_PARENS.into(), ")");
1055
1056 builder.finish_node();
1057 }
1058
1059 builder.finish_node();
1060 Relation(SyntaxNode::new_root_mut(builder.finish()))
1061 }
1062
1063 #[must_use]
1072 pub fn wrap_and_sort(&self) -> Self {
1073 let mut builder = GreenNodeBuilder::new();
1074 builder.start_node(SyntaxKind::RELATION.into());
1075 builder.token(IDENT.into(), self.name().as_str());
1076 if let Some((vc, version)) = self.version() {
1077 builder.token(WHITESPACE.into(), " ");
1078 builder.start_node(SyntaxKind::VERSION.into());
1079 builder.token(L_PARENS.into(), "(");
1080 builder.start_node(SyntaxKind::CONSTRAINT.into());
1081 push_constraint_tokens(&mut builder, &vc);
1082 builder.finish_node();
1083 builder.token(WHITESPACE.into(), " ");
1084 builder.token(IDENT.into(), version.to_string().as_str());
1085 builder.token(R_PARENS.into(), ")");
1086 builder.finish_node();
1087 }
1088 builder.finish_node();
1089 Relation(SyntaxNode::new_root_mut(builder.finish()))
1090 }
1091
1092 pub fn simple(name: &str) -> Self {
1101 Self::new(name, None)
1102 }
1103
1104 pub fn drop_constraint(&mut self) -> bool {
1115 let version_token = self.0.children().find(|n| n.kind() == VERSION);
1116 if let Some(version_token) = version_token {
1117 while let Some(prev) = version_token.prev_sibling_or_token() {
1119 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
1120 prev.detach();
1121 } else {
1122 break;
1123 }
1124 }
1125 version_token.detach();
1126 return true;
1127 }
1128
1129 false
1130 }
1131
1132 pub fn name(&self) -> String {
1141 self.0
1142 .children_with_tokens()
1143 .find_map(|it| match it {
1144 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
1145 _ => None,
1146 })
1147 .unwrap()
1148 .text()
1149 .to_string()
1150 }
1151
1152 pub fn version(&self) -> Option<(VersionConstraint, Version)> {
1154 let vc = self.0.children().find(|n| n.kind() == VERSION);
1155 let vc = vc.as_ref()?;
1156 let constraint = vc.children().find(|n| n.kind() == CONSTRAINT);
1157
1158 let version = vc.children_with_tokens().find_map(|it| match it {
1159 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
1160 _ => None,
1161 });
1162
1163 if let (Some(constraint), Some(version)) = (constraint, version) {
1164 let vc: VersionConstraint = constraint.to_string().parse().unwrap();
1165 Some((vc, (version.text().to_string()).parse().unwrap()))
1166 } else {
1167 None
1168 }
1169 }
1170
1171 pub fn set_version(&mut self, version_constraint: Option<(VersionConstraint, Version)>) {
1182 let current_version = self.0.children().find(|n| n.kind() == VERSION);
1183 if let Some((vc, version)) = version_constraint {
1184 let mut builder = GreenNodeBuilder::new();
1185 builder.start_node(VERSION.into());
1186 builder.token(L_PARENS.into(), "(");
1187 builder.start_node(CONSTRAINT.into());
1188 push_constraint_tokens(&mut builder, &vc);
1189 builder.finish_node(); builder.token(WHITESPACE.into(), " ");
1191 builder.token(IDENT.into(), version.to_string().as_str());
1192 builder.token(R_PARENS.into(), ")");
1193 builder.finish_node(); if let Some(current_version) = current_version {
1196 self.0.splice_children(
1197 current_version.index()..current_version.index() + 1,
1198 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
1199 );
1200 } else {
1201 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
1202 let idx = if let Some(name_node) = name_node {
1203 name_node.index() + 1
1204 } else {
1205 0
1206 };
1207 let new_children = vec![
1208 GreenToken::new(WHITESPACE.into(), " ").into(),
1209 builder.finish().into(),
1210 ];
1211 let new_root = SyntaxNode::new_root_mut(
1212 self.0.green().splice_children(idx..idx, new_children),
1213 );
1214 if let Some(parent) = self.0.parent() {
1215 parent.splice_children(
1216 self.0.index()..self.0.index() + 1,
1217 vec![new_root.into()],
1218 );
1219 self.0 = parent
1220 .children_with_tokens()
1221 .nth(self.0.index())
1222 .unwrap()
1223 .clone()
1224 .into_node()
1225 .unwrap();
1226 } else {
1227 self.0 = new_root;
1228 }
1229 }
1230 } else if let Some(current_version) = current_version {
1231 while let Some(prev) = current_version.prev_sibling_or_token() {
1233 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
1234 prev.detach();
1235 } else {
1236 break;
1237 }
1238 }
1239 current_version.detach();
1240 }
1241 }
1242
1243 pub fn remove(&mut self) {
1255 let is_first = !self
1256 .0
1257 .siblings(Direction::Prev)
1258 .skip(1)
1259 .any(|n| n.kind() == RELATION);
1260 if !is_first {
1261 while let Some(n) = self.0.prev_sibling_or_token() {
1264 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1265 n.detach();
1266 } else if n.kind() == COMMA {
1267 n.detach();
1268 break;
1269 } else {
1270 break;
1271 }
1272 }
1273 while let Some(n) = self.0.prev_sibling_or_token() {
1274 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1275 n.detach();
1276 } else {
1277 break;
1278 }
1279 }
1280 } else {
1281 while let Some(n) = self.0.next_sibling_or_token() {
1284 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1285 n.detach();
1286 } else if n.kind() == COMMA {
1287 n.detach();
1288 break;
1289 } else {
1290 panic!("Unexpected node: {n:?}");
1291 }
1292 }
1293
1294 while let Some(n) = self.0.next_sibling_or_token() {
1295 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1296 n.detach();
1297 } else {
1298 break;
1299 }
1300 }
1301 }
1302 self.0.detach();
1303 }
1304
1305 pub fn satisfied_by(
1307 &self,
1308 package_version: impl crate::relations::VersionLookup + Copy,
1309 ) -> bool {
1310 let name = self.name();
1311 let version = self.version();
1312 if let Some(version) = version {
1313 if let Some(package_version) = package_version.lookup_version(&name) {
1314 let required: LossyVersion = version.1.into();
1315 let actual = package_version.into_owned();
1316 match version.0 {
1317 VersionConstraint::GreaterThanEqual => actual >= required,
1318 VersionConstraint::LessThanEqual => actual <= required,
1319 VersionConstraint::Equal => actual == required,
1320 VersionConstraint::NotEqual => actual != required,
1321 VersionConstraint::GreaterThan => actual > required,
1322 VersionConstraint::LessThan => actual < required,
1323 }
1324 } else {
1325 false
1326 }
1327 } else {
1328 true
1329 }
1330 }
1331 }
1332
1333 impl PartialOrd for Relation {
1334 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1335 Some(self.cmp(other))
1336 }
1337 }
1338
1339 impl Eq for Relation {}
1340
1341 impl Ord for Relation {
1342 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1343 let name_cmp = self.name().cmp(&other.name());
1345 if name_cmp != std::cmp::Ordering::Equal {
1346 return name_cmp;
1347 }
1348
1349 let self_version = self.version();
1350 let other_version = other.version();
1351
1352 match (self_version, other_version) {
1353 (Some((self_vc, self_version)), Some((other_vc, other_version))) => {
1354 let vc_cmp = self_vc.cmp(&other_vc);
1355 if vc_cmp != std::cmp::Ordering::Equal {
1356 return vc_cmp;
1357 }
1358
1359 self_version.cmp(&other_version)
1360 }
1361 (Some(_), None) => std::cmp::Ordering::Greater,
1362 (None, Some(_)) => std::cmp::Ordering::Less,
1363 (None, None) => std::cmp::Ordering::Equal,
1364 }
1365 }
1366 }
1367
1368 impl std::str::FromStr for Relations {
1369 type Err = String;
1370
1371 fn from_str(s: &str) -> Result<Self, Self::Err> {
1372 let parse = parse(s);
1373 if parse.errors.is_empty() {
1374 Ok(parse.root_mut())
1375 } else {
1376 Err(parse.errors.join("\n"))
1377 }
1378 }
1379 }
1380
1381 impl std::str::FromStr for Relation {
1382 type Err = String;
1383
1384 fn from_str(s: &str) -> Result<Self, Self::Err> {
1385 let rels = s.parse::<Relations>()?;
1386 let mut relations = rels.relations();
1387
1388 let relation = if let Some(relation) = relations.next() {
1389 relation
1390 } else {
1391 return Err("No relation found".to_string());
1392 };
1393
1394 if relations.next().is_some() {
1395 return Err("Multiple relations found".to_string());
1396 }
1397
1398 Ok(relation)
1399 }
1400 }
1401
1402 #[cfg(test)]
1403 mod tests {
1404 use super::*;
1405
1406 #[test]
1407 fn test_parse() {
1408 let input = "cli";
1409 let parsed: Relations = input.parse().unwrap();
1410 assert_eq!(parsed.to_string(), input);
1411 assert_eq!(parsed.relations().count(), 1);
1412 let relation = parsed.relations().next().unwrap();
1413 assert_eq!(relation.to_string(), "cli");
1414 assert_eq!(relation.version(), None);
1415
1416 let input = "cli (>= 0.20.21)";
1417 let parsed: Relations = input.parse().unwrap();
1418 assert_eq!(parsed.to_string(), input);
1419 assert_eq!(parsed.relations().count(), 1);
1420 let relation = parsed.relations().next().unwrap();
1421 assert_eq!(relation.to_string(), "cli (>= 0.20.21)");
1422 assert_eq!(
1423 relation.version(),
1424 Some((
1425 VersionConstraint::GreaterThanEqual,
1426 "0.20.21".parse().unwrap()
1427 ))
1428 );
1429
1430 let input = "xml2 (> 1.0.0)";
1431 let parsed: Relations = input.parse().unwrap();
1432 assert_eq!(parsed.to_string(), input);
1433 let relation = parsed.relations().next().unwrap();
1434 assert_eq!(
1435 relation.version(),
1436 Some((VersionConstraint::GreaterThan, "1.0.0".parse().unwrap()))
1437 );
1438
1439 let input = "xml2 (< 2.0.0)";
1440 let parsed: Relations = input.parse().unwrap();
1441 assert_eq!(parsed.to_string(), input);
1442 let relation = parsed.relations().next().unwrap();
1443 assert_eq!(
1444 relation.version(),
1445 Some((VersionConstraint::LessThan, "2.0.0".parse().unwrap()))
1446 );
1447 }
1448
1449 #[test]
1450 fn test_r_equality_operators() {
1451 let input = "xml2 (== 1.0.0)";
1452 let parsed: Relations = input.parse().unwrap();
1453 assert_eq!(parsed.to_string(), input);
1454 let relation = parsed.relations().next().unwrap();
1455 assert_eq!(
1456 relation.version(),
1457 Some((VersionConstraint::Equal, "1.0.0".parse().unwrap()))
1458 );
1459
1460 let input = "xml2 (!= 1.0.0)";
1461 let parsed: Relations = input.parse().unwrap();
1462 assert_eq!(parsed.to_string(), input);
1463 let relation = parsed.relations().next().unwrap();
1464 assert_eq!(
1465 relation.version(),
1466 Some((VersionConstraint::NotEqual, "1.0.0".parse().unwrap()))
1467 );
1468 }
1469
1470 #[test]
1471 fn test_constructed_relations_use_r_operators() {
1472 let cases = [
1473 (VersionConstraint::LessThan, "xml2 (< 1.0)"),
1474 (VersionConstraint::LessThanEqual, "xml2 (<= 1.0)"),
1475 (VersionConstraint::Equal, "xml2 (== 1.0)"),
1476 (VersionConstraint::NotEqual, "xml2 (!= 1.0)"),
1477 (VersionConstraint::GreaterThan, "xml2 (> 1.0)"),
1478 (VersionConstraint::GreaterThanEqual, "xml2 (>= 1.0)"),
1479 ];
1480 for (vc, expected) in cases {
1481 let relation = Relation::new("xml2", Some((vc, "1.0".parse().unwrap())));
1482 assert_eq!(relation.to_string(), expected);
1483 }
1484 }
1485
1486 #[test]
1487 fn test_multiple() {
1488 let input = "cli (>= 0.20.21), cli (< 0.21)";
1489 let parsed: Relations = input.parse().unwrap();
1490 assert_eq!(parsed.to_string(), input);
1491 assert_eq!(parsed.relations().count(), 2);
1492 let relation = parsed.relations().next().unwrap();
1493 assert_eq!(relation.to_string(), "cli (>= 0.20.21)");
1494 assert_eq!(
1495 relation.version(),
1496 Some((
1497 VersionConstraint::GreaterThanEqual,
1498 "0.20.21".parse().unwrap()
1499 ))
1500 );
1501 let relation = parsed.relations().nth(1).unwrap();
1502 assert_eq!(relation.to_string(), "cli (< 0.21)");
1503 assert_eq!(
1504 relation.version(),
1505 Some((VersionConstraint::LessThan, "0.21".parse().unwrap()))
1506 );
1507 }
1508
1509 #[test]
1510 fn test_new() {
1511 let r = Relation::new(
1512 "cli",
1513 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
1514 );
1515
1516 assert_eq!(r.to_string(), "cli (>= 2.0)");
1517 }
1518
1519 #[test]
1520 fn test_drop_constraint() {
1521 let mut r = Relation::new(
1522 "cli",
1523 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
1524 );
1525
1526 r.drop_constraint();
1527
1528 assert_eq!(r.to_string(), "cli");
1529 }
1530
1531 #[test]
1532 fn test_simple() {
1533 let r = Relation::simple("cli");
1534
1535 assert_eq!(r.to_string(), "cli");
1536 }
1537
1538 #[test]
1539 fn test_remove_first_relation() {
1540 let mut rels: Relations = r#"cli (>= 0.20.21), cli (< 0.21)"#.parse().unwrap();
1541 let removed = rels.remove_relation(0);
1542 assert_eq!(removed.to_string(), "cli (>= 0.20.21)");
1543 assert_eq!(rels.to_string(), "cli (< 0.21)");
1544 }
1545
1546 #[test]
1547 fn test_remove_last_relation() {
1548 let mut rels: Relations = r#"cli (>= 0.20.21), cli (< 0.21)"#.parse().unwrap();
1549 rels.remove_relation(1);
1550 assert_eq!(rels.to_string(), "cli (>= 0.20.21)");
1551 }
1552
1553 #[test]
1554 fn test_remove_middle() {
1555 let mut rels: Relations =
1556 r#"cli (>= 0.20.21), cli (< 0.21), cli (< 0.22)"#.parse().unwrap();
1557 rels.remove_relation(1);
1558 assert_eq!(rels.to_string(), "cli (>= 0.20.21), cli (< 0.22)");
1559 }
1560
1561 #[test]
1562 fn test_remove_added() {
1563 let mut rels: Relations = r#"cli (>= 0.20.21)"#.parse().unwrap();
1564 let relation = Relation::simple("cli");
1565 rels.push(relation);
1566 rels.remove_relation(1);
1567 assert_eq!(rels.to_string(), "cli (>= 0.20.21)");
1568 }
1569
1570 #[test]
1571 fn test_push() {
1572 let mut rels: Relations = r#"cli (>= 0.20.21)"#.parse().unwrap();
1573 let relation = Relation::simple("cli");
1574 rels.push(relation);
1575 assert_eq!(rels.to_string(), "cli (>= 0.20.21), cli");
1576 }
1577
1578 #[test]
1579 fn test_push_from_empty() {
1580 let mut rels: Relations = "".parse().unwrap();
1581 let relation = Relation::simple("cli");
1582 rels.push(relation);
1583 assert_eq!(rels.to_string(), "cli");
1584 }
1585
1586 #[test]
1587 fn test_insert() {
1588 let mut rels: Relations = r#"cli (>= 0.20.21), cli (< 0.21)"#.parse().unwrap();
1589 let relation = Relation::simple("cli");
1590 rels.insert(1, relation);
1591 assert_eq!(rels.to_string(), "cli (>= 0.20.21), cli, cli (< 0.21)");
1592 }
1593
1594 #[test]
1595 fn test_insert_at_start() {
1596 let mut rels: Relations = r#"cli (>= 0.20.21), cli (< 0.21)"#.parse().unwrap();
1597 let relation = Relation::simple("cli");
1598 rels.insert(0, relation);
1599 assert_eq!(rels.to_string(), "cli, cli (>= 0.20.21), cli (< 0.21)");
1600 }
1601
1602 #[test]
1603 fn test_insert_after_error() {
1604 let (mut rels, errors) = Relations::parse_relaxed("@foo@, debhelper (>= 1.0)");
1605 assert_eq!(
1606 errors,
1607 vec![
1608 "expected identifier or comma but got ERROR",
1609 "expected comma or end of file but got Some(IDENT)",
1610 "expected identifier or comma but got ERROR"
1611 ]
1612 );
1613 let relation = Relation::simple("bar");
1614 rels.push(relation);
1615 assert_eq!(rels.to_string(), "@foo@, debhelper (>= 1.0), bar");
1616 }
1617
1618 #[test]
1619 fn test_insert_before_error() {
1620 let (mut rels, errors) = Relations::parse_relaxed("debhelper (>= 1.0), @foo@, bla");
1621 assert_eq!(
1622 errors,
1623 vec![
1624 "expected identifier or comma but got ERROR",
1625 "expected comma or end of file but got Some(IDENT)",
1626 "expected identifier or comma but got ERROR"
1627 ]
1628 );
1629 let relation = Relation::simple("bar");
1630 rels.insert(0, relation);
1631 assert_eq!(rels.to_string(), "bar, debhelper (>= 1.0), @foo@, bla");
1632 }
1633
1634 #[test]
1635 fn test_replace() {
1636 let mut rels: Relations = r#"cli (>= 0.20.21), cli (< 0.21)"#.parse().unwrap();
1637 let relation = Relation::simple("cli");
1638 rels.replace(1, relation);
1639 assert_eq!(rels.to_string(), "cli (>= 0.20.21), cli");
1640 }
1641
1642 #[test]
1643 fn test_parse_relation() {
1644 let parsed: Relation = "cli (>= 0.20.21)".parse().unwrap();
1645 assert_eq!(parsed.to_string(), "cli (>= 0.20.21)");
1646 assert_eq!(
1647 parsed.version(),
1648 Some((
1649 VersionConstraint::GreaterThanEqual,
1650 "0.20.21".parse().unwrap()
1651 ))
1652 );
1653 assert_eq!(
1654 "foo, bar".parse::<Relation>().unwrap_err(),
1655 "Multiple relations found"
1656 );
1657 assert_eq!("".parse::<Relation>().unwrap_err(), "No relation found");
1658 }
1659
1660 #[test]
1661 fn test_parse_relation_preserves_version_separators() {
1662 let parsed: Relation = "cli (>= 2.5-1)".parse().unwrap();
1663 let (_, version) = parsed.version().unwrap();
1664
1665 assert_eq!(version.to_string(), "2.5-1");
1666 assert_eq!(parsed.wrap_and_sort().to_string(), "cli (>= 2.5-1)");
1667 assert_eq!(version, "2.5.1".parse::<Version>().unwrap());
1668 }
1669
1670 #[test]
1671 fn test_relations_satisfied_by() {
1672 let rels: Relations = "cli (>= 0.20.21), cli (< 0.21)".parse().unwrap();
1673 let satisfied = |name: &str| -> Option<LossyVersion> {
1674 match name {
1675 "cli" => Some("0.20.21".parse().unwrap()),
1676 _ => None,
1677 }
1678 };
1679 assert!(rels.satisfied_by(satisfied));
1680
1681 let satisfied = |name: &str| match name {
1682 "cli" => Some("0.21".parse().unwrap()),
1683 _ => None,
1684 };
1685 assert!(!rels.satisfied_by(satisfied));
1686
1687 let satisfied = |name: &str| match name {
1688 "cli" => Some("0.20.20".parse().unwrap()),
1689 _ => None,
1690 };
1691 assert!(!rels.satisfied_by(satisfied));
1692 }
1693
1694 #[test]
1695 fn test_wrap_and_sort_relation() {
1696 let relation: Relation = " cli (>= 11.0)".parse().unwrap();
1697
1698 let wrapped = relation.wrap_and_sort();
1699
1700 assert_eq!(wrapped.to_string(), "cli (>= 11.0)");
1701 }
1702
1703 #[test]
1704 fn test_wrap_and_sort_relations() {
1705 let relations: Relations = "cli (>= 0.20.21) , \n\n\n\ncli (< 0.21)".parse().unwrap();
1706
1707 let wrapped = relations.wrap_and_sort();
1708
1709 assert_eq!(wrapped.to_string(), "cli (< 0.21), cli (>= 0.20.21)");
1710 }
1711
1712 #[cfg(feature = "serde")]
1713 #[test]
1714 fn test_serialize_relations() {
1715 let relations: Relations = "cli (>= 0.20.21), cli (< 0.21)".parse().unwrap();
1716 let serialized = serde_json::to_string(&relations).unwrap();
1717 assert_eq!(serialized, r#""cli (>= 0.20.21), cli (< 0.21)""#);
1718 }
1719
1720 #[cfg(feature = "serde")]
1721 #[test]
1722 fn test_deserialize_relations() {
1723 let relations: Relations = "cli (>= 0.20.21), cli (< 0.21)".parse().unwrap();
1724 let serialized = serde_json::to_string(&relations).unwrap();
1725 let deserialized: Relations = serde_json::from_str(&serialized).unwrap();
1726 assert_eq!(deserialized.to_string(), relations.to_string());
1727 }
1728
1729 #[cfg(feature = "serde")]
1730 #[test]
1731 fn test_serialize_relation() {
1732 let relation: Relation = "cli (>= 0.20.21)".parse().unwrap();
1733 let serialized = serde_json::to_string(&relation).unwrap();
1734 assert_eq!(serialized, r#""cli (>= 0.20.21)""#);
1735 }
1736
1737 #[cfg(feature = "serde")]
1738 #[test]
1739 fn test_deserialize_relation() {
1740 let relation: Relation = "cli (>= 0.20.21)".parse().unwrap();
1741 let serialized = serde_json::to_string(&relation).unwrap();
1742 let deserialized: Relation = serde_json::from_str(&serialized).unwrap();
1743 assert_eq!(deserialized.to_string(), relation.to_string());
1744 }
1745
1746 #[test]
1747 fn test_relation_set_version() {
1748 let mut rel: Relation = "vign".parse().unwrap();
1749 rel.set_version(None);
1750 assert_eq!("vign", rel.to_string());
1751 rel.set_version(Some((
1752 VersionConstraint::GreaterThanEqual,
1753 "2.0".parse().unwrap(),
1754 )));
1755 assert_eq!("vign (>= 2.0)", rel.to_string());
1756 rel.set_version(None);
1757 assert_eq!("vign", rel.to_string());
1758 rel.set_version(Some((
1759 VersionConstraint::GreaterThanEqual,
1760 "2.0".parse().unwrap(),
1761 )));
1762 rel.set_version(Some((
1763 VersionConstraint::GreaterThanEqual,
1764 "1.1".parse().unwrap(),
1765 )));
1766 assert_eq!("vign (>= 1.1)", rel.to_string());
1767 }
1768
1769 #[test]
1770 fn test_wrap_and_sort_removes_empty_entries() {
1771 let relations: Relations = "foo, , bar, ".parse().unwrap();
1772 let wrapped = relations.wrap_and_sort();
1773 assert_eq!(wrapped.to_string(), "bar, foo");
1774 }
1775 }
1776}
1777
1778#[cfg(test)]
1779mod tests {
1780 use super::*;
1781
1782 #[test]
1783 fn test_parse() {
1784 let s = r###"Package: mypackage
1785Title: What the Package Does (One Line, Title Case)
1786Version: 0.0.0.9000
1787Authors@R:
1788 person("First", "Last", , "first.last@example.com", role = c("aut", "cre"),
1789 comment = c(ORCID = "YOUR-ORCID-ID"))
1790Description: What the package does (one paragraph).
1791License: `use_mit_license()`, `use_gpl3_license()` or friends to pick a
1792 license
1793Encoding: UTF-8
1794Roxygen: list(markdown = TRUE)
1795RoxygenNote: 7.3.2
1796"###;
1797 let desc: RDescription = s.parse().unwrap();
1798
1799 assert_eq!(desc.package(), Some("mypackage".to_string()));
1800 assert_eq!(
1801 desc.title(),
1802 Some("What the Package Does (One Line, Title Case)".to_string())
1803 );
1804 assert_eq!(desc.version(), Some("0.0.0.9000".to_string()));
1805 assert_eq!(
1806 desc.authors(),
1807 Some(RCode(
1808 r#"person("First", "Last", , "first.last@example.com", role = c("aut", "cre"),
1809comment = c(ORCID = "YOUR-ORCID-ID"))"#
1810 .to_string()
1811 ))
1812 );
1813 assert_eq!(
1814 desc.description(),
1815 Some("What the package does (one paragraph).".to_string())
1816 );
1817 assert_eq!(
1818 desc.license(),
1819 Some(
1820 "`use_mit_license()`, `use_gpl3_license()` or friends to pick a\nlicense"
1821 .to_string()
1822 )
1823 );
1824 assert_eq!(desc.encoding(), Some("UTF-8".to_string()));
1825 assert_eq!(desc.roxygen(), Some("list(markdown = TRUE)".to_string()));
1826 assert_eq!(desc.roxygen_note(), Some("7.3.2".to_string()));
1827
1828 assert_eq!(desc.to_string(), s);
1829 }
1830
1831 #[test]
1832 fn test_setters_replace_existing_fields() {
1833 let s = r###"Package: mypackage
1834Title: What the Package Does (One Line, Title Case)
1835Version: 0.0.0.9000
1836Description: What the package does (one paragraph).
1837Encoding: UTF-8
1838"###;
1839 let mut desc: RDescription = s.parse().unwrap();
1840
1841 desc.set_version("0.0.1");
1842
1843 assert_eq!(desc.version(), Some("0.0.1".to_string()));
1845 assert_eq!(desc.to_string().matches("Version:").count(), 1);
1846
1847 assert_eq!(
1848 desc.to_string(),
1849 r###"Package: mypackage
1850Title: What the Package Does (One Line, Title Case)
1851Version: 0.0.1
1852Description: What the package does (one paragraph).
1853Encoding: UTF-8
1854"###
1855 );
1856 }
1857
1858 #[test]
1859 fn test_setters_add_missing_fields() {
1860 let s = "Package: mypackage\n";
1861 let mut desc: RDescription = s.parse().unwrap();
1862
1863 desc.set_version("0.0.1");
1864
1865 assert_eq!(desc.version(), Some("0.0.1".to_string()));
1866 assert_eq!(desc.to_string(), "Package: mypackage\nVersion: 0.0.1\n");
1867 }
1868
1869 #[test]
1870 fn test_parse_dplyr() {
1871 let s = include_str!("../testdata/dplyr.desc");
1872
1873 let desc: RDescription = s.parse().unwrap();
1874 assert_eq!("dplyr", desc.package().unwrap());
1875 assert_eq!(
1876 "https://dplyr.tidyverse.org, https://github.com/tidyverse/dplyr",
1877 desc.url().unwrap().as_str()
1878 );
1879 }
1880
1881 #[test]
1882 fn test_dependency_style_fields_return_relations() {
1883 let s = r###"Package: mypackage
1884Title: What the Package Does
1885Version: 1.0.0
1886Description: What the package does.
1887License: MIT
1888Imports: cli (>= 2.5-1), glue
1889Suggests: testthat (>= 3.0.0)
1890Depends: R (>= 4.1.0)
1891LinkingTo: cpp11 (>= 0.4-1)
1892Enhances: shiny
1893"###;
1894 let desc: RDescription = s.parse().unwrap();
1895
1896 let imports = desc.imports().unwrap();
1897 assert_eq!(imports.len(), 2);
1898 assert_eq!(
1899 imports
1900 .iter()
1901 .next()
1902 .unwrap()
1903 .version()
1904 .unwrap()
1905 .1
1906 .to_string(),
1907 "2.5-1"
1908 );
1909 assert_eq!(desc.suggests().unwrap().len(), 1);
1910 assert_eq!(desc.depends().unwrap().len(), 1);
1911 assert_eq!(
1912 desc.linking_to()
1913 .unwrap()
1914 .iter()
1915 .next()
1916 .unwrap()
1917 .version()
1918 .unwrap()
1919 .1
1920 .to_string(),
1921 "0.4-1"
1922 );
1923 assert_eq!(
1924 desc.enhances().unwrap().iter().next().unwrap().name(),
1925 "shiny"
1926 );
1927 }
1928
1929 #[test]
1930 fn test_r_description_additional_repositories() {
1931 let desc: RDescription = r###"Package: mypackage
1932Title: What the Package Does
1933Version: 1.0.0
1934Description: What the package does.
1935License: MIT
1936Additional_repositories: https://example.com/src/contrib,
1937 https://example.org/src/contrib
1938"###
1939 .parse()
1940 .unwrap();
1941
1942 assert_eq!(
1943 desc.additional_repositories(),
1944 Some(vec![
1945 "https://example.com/src/contrib".to_string(),
1946 "https://example.org/src/contrib".to_string(),
1947 ])
1948 );
1949 }
1950
1951 #[test]
1952 fn test_set_additional_repositories() {
1953 let mut desc: RDescription = r###"Package: mypackage
1954Title: What the Package Does
1955Version: 1.0.0
1956Description: What the package does.
1957License: MIT
1958"###
1959 .parse()
1960 .unwrap();
1961
1962 desc.set_additional_repositories(&[
1963 "https://example.com/src/contrib",
1964 "https://example.org/src/contrib",
1965 ]);
1966
1967 assert_eq!(
1968 desc.additional_repositories(),
1969 Some(vec![
1970 "https://example.com/src/contrib".to_string(),
1971 "https://example.org/src/contrib".to_string(),
1972 ])
1973 );
1974 assert_eq!(
1975 desc.to_string(),
1976 "Package: mypackage\nTitle: What the Package Does\nVersion: 1.0.0\nDescription: What the package does.\nLicense: MIT\nAdditional_repositories: https://example.com/src/contrib, https://example.org/src/contrib\n"
1977 );
1978 }
1979}