1use crate::RCode;
13use deb822_lossless::Paragraph;
14pub use relations::{Relation, Relations};
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
31#[derive(Debug)]
32pub enum Error {
34 Io(std::io::Error),
36
37 Parse(deb822_lossless::ParseError),
39}
40
41impl std::fmt::Display for Error {
42 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
43 match self {
44 Self::Io(e) => write!(f, "IO error: {e}"),
45 Self::Parse(e) => write!(f, "Parse error: {e}"),
46 }
47 }
48}
49
50impl std::error::Error for Error {}
51
52impl From<deb822_lossless::ParseError> for Error {
53 fn from(e: deb822_lossless::ParseError) -> Self {
54 Self::Parse(e)
55 }
56}
57
58impl From<std::io::Error> for Error {
59 fn from(e: std::io::Error) -> Self {
60 Self::Io(e)
61 }
62}
63
64impl std::str::FromStr for RDescription {
65 type Err = Error;
66
67 fn from_str(s: &str) -> Result<Self, Self::Err> {
68 Ok(Self(Paragraph::from_str(s)?))
69 }
70}
71
72impl RDescription {
73 pub fn new() -> Self {
75 Self(Paragraph::new())
76 }
77
78 pub fn package(&self) -> Option<String> {
80 self.0.get("Package")
81 }
82
83 pub fn set_package(&mut self, package: &str) {
85 self.0.insert("Package", package);
86 }
87
88 pub fn title(&self) -> Option<String> {
93 self.0.get("Title")
94 }
95
96 pub fn maintainer(&self) -> Option<String> {
98 self.0.get("Maintainer")
99 }
100
101 pub fn set_maintainer(&mut self, maintainer: &str) {
103 self.0.insert("Maintainer", maintainer);
104 }
105
106 pub fn authors(&self) -> Option<RCode> {
108 self.0.get("Authors@R").map(|s| s.parse().unwrap())
109 }
110
111 pub fn set_authors(&mut self, authors: &RCode) {
113 self.0.insert("Authors@R", &authors.to_string());
114 }
115
116 pub fn set_title(&mut self, title: &str) {
118 self.0.insert("Title", title);
119 }
120
121 pub fn description(&self) -> Option<String> {
123 self.0.get("Description")
124 }
125
126 pub fn set_description(&mut self, description: &str) {
128 self.0.insert("Description", description);
129 }
130
131 pub fn version(&self) -> Option<String> {
133 self.0.get("Version")
134 }
135
136 pub fn set_version(&mut self, version: &str) {
138 self.0.insert("Version", version);
139 }
140
141 pub fn encoding(&self) -> Option<String> {
143 self.0.get("Encoding")
144 }
145
146 pub fn set_encoding(&mut self, encoding: &str) {
148 self.0.insert("Encoding", encoding);
149 }
150
151 pub fn license(&self) -> Option<String> {
153 self.0.get("License")
154 }
155
156 pub fn set_license(&mut self, license: &str) {
158 self.0.insert("License", license);
159 }
160
161 pub fn roxygen_note(&self) -> Option<String> {
163 self.0.get("RoxygenNote")
164 }
165
166 pub fn set_roxygen_note(&mut self, roxygen_note: &str) {
168 self.0.insert("RoxygenNote", roxygen_note);
169 }
170
171 pub fn roxygen(&self) -> Option<String> {
173 self.0.get("Roxygen")
174 }
175
176 pub fn set_roxygen(&mut self, roxygen: &str) {
178 self.0.insert("Roxygen", roxygen);
179 }
180
181 pub fn url(&self) -> Option<String> {
183 self.0.get("URL")
185 }
186
187 pub fn set_url(&mut self, url: &str) {
189 self.0.insert("URL", url);
191 }
192
193 pub fn bug_reports(&self) -> Option<url::Url> {
195 self.0
196 .get("BugReports")
197 .map(|s| url::Url::parse(s.as_str()).unwrap())
198 }
199
200 pub fn set_bug_reports(&mut self, bug_reports: &url::Url) {
202 self.0.insert("BugReports", bug_reports.as_str());
203 }
204
205 pub fn imports(&self) -> Option<Vec<String>> {
207 self.0
208 .get("Imports")
209 .map(|s| s.split(',').map(|s| s.trim().to_string()).collect())
210 }
211
212 pub fn set_imports(&mut self, imports: &[&str]) {
214 self.0.insert("Imports", &imports.join(", "));
215 }
216
217 pub fn suggests(&self) -> Option<Relations> {
219 self.0.get("Suggests").map(|s| s.parse().unwrap())
220 }
221
222 pub fn set_suggests(&mut self, suggests: Relations) {
224 self.0.insert("Suggests", &suggests.to_string());
225 }
226
227 pub fn depends(&self) -> Option<Relations> {
229 self.0.get("Depends").map(|s| s.parse().unwrap())
230 }
231
232 pub fn set_depends(&mut self, depends: Relations) {
234 self.0.insert("Depends", &depends.to_string());
235 }
236
237 pub fn linking_to(&self) -> Option<Vec<String>> {
239 self.0
240 .get("LinkingTo")
241 .map(|s| s.split(',').map(|s| s.trim().to_string()).collect())
242 }
243
244 pub fn set_linking_to(&mut self, linking_to: &[&str]) {
246 self.0.insert("LinkingTo", &linking_to.join(", "));
247 }
248
249 pub fn lazy_data(&self) -> Option<bool> {
251 self.0.get("LazyData").map(|s| s == "true")
252 }
253
254 pub fn set_lazy_data(&mut self, lazy_data: bool) {
256 self.0
257 .insert("LazyData", if lazy_data { "true" } else { "false" });
258 }
259
260 pub fn collate(&self) -> Option<String> {
262 self.0.get("Collate")
263 }
264
265 pub fn set_collate(&mut self, collate: &str) {
267 self.0.insert("Collate", collate);
268 }
269
270 pub fn vignette_builder(&self) -> Option<Vec<String>> {
272 self.0
273 .get("VignetteBuilder")
274 .map(|s| s.split(',').map(|s| s.trim().to_string()).collect())
275 }
276
277 pub fn set_vignette_builder(&mut self, vignette_builder: &[&str]) {
279 self.0
280 .insert("VignetteBuilder", &vignette_builder.join(", "));
281 }
282
283 pub fn system_requirements(&self) -> Option<Vec<String>> {
285 self.0
286 .get("SystemRequirements")
287 .map(|s| s.split(',').map(|s| s.trim().to_string()).collect())
288 }
289
290 pub fn set_system_requirements(&mut self, system_requirements: &[&str]) {
292 self.0
293 .insert("SystemRequirements", &system_requirements.join(", "));
294 }
295
296 pub fn date(&self) -> Option<String> {
298 self.0.get("Date")
299 }
300
301 pub fn set_date(&mut self, date: &str) {
303 self.0.insert("Date", date);
304 }
305
306 pub fn repository(&self) -> Option<String> {
310 self.0.get("Repository")
311 }
312
313 pub fn set_repository(&mut self, repository: &str) {
315 self.0.insert("Repository", repository);
316 }
317}
318
319pub mod relations {
320 use crate::relations::SyntaxKind::{self, *};
339 use crate::relations::VersionConstraint;
340 use crate::version::Version;
341 use rowan::{Direction, NodeOrToken};
342
343 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
345 pub struct ParseError(Vec<String>);
346
347 impl std::fmt::Display for ParseError {
348 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
349 for err in &self.0 {
350 writeln!(f, "{err}")?;
351 }
352 Ok(())
353 }
354 }
355
356 impl std::error::Error for ParseError {}
357
358 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
362 enum Lang {}
363 impl rowan::Language for Lang {
364 type Kind = SyntaxKind;
365 fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
366 unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
367 }
368 fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
369 kind.into()
370 }
371 }
372
373 use rowan::{GreenNode, GreenToken};
376
377 use rowan::GreenNodeBuilder;
381
382 fn push_constraint_tokens(builder: &mut GreenNodeBuilder, vc: &VersionConstraint) {
386 for c in vc.to_string().chars() {
387 let kind = match c {
388 '>' => R_ANGLE,
389 '<' => L_ANGLE,
390 '=' => EQUAL,
391 '!' => NOT,
392 _ => unreachable!("unexpected constraint character {c:?}"),
393 };
394 builder.token(kind.into(), c.to_string().as_str());
395 }
396 }
397
398 struct Parse {
401 green_node: GreenNode,
402 #[allow(unused)]
403 errors: Vec<String>,
404 }
405
406 fn parse(text: &str) -> Parse {
407 struct Parser {
408 tokens: Vec<(SyntaxKind, String)>,
411 builder: GreenNodeBuilder<'static>,
413 errors: Vec<String>,
416 }
417
418 impl Parser {
419 fn error(&mut self, error: String) {
420 self.errors.push(error);
421 self.builder.start_node(SyntaxKind::ERROR.into());
422 if self.current().is_some() {
423 self.bump();
424 }
425 self.builder.finish_node();
426 }
427
428 fn parse_relation(&mut self) {
429 self.builder.start_node(SyntaxKind::RELATION.into());
430 if self.current() == Some(IDENT) {
431 self.bump();
432 } else {
433 self.error("Expected package name".to_string());
434 }
435 match self.peek_past_ws() {
436 Some(COMMA) => {}
437 None | Some(L_PARENS) => {
438 self.skip_ws();
439 }
440 e => {
441 self.skip_ws();
442 self.error(format!(
443 "Expected ':' or '|' or '[' or '<' or ',' but got {e:?}"
444 ));
445 }
446 }
447
448 if self.peek_past_ws() == Some(L_PARENS) {
449 self.skip_ws();
450 self.builder.start_node(VERSION.into());
451 self.bump();
452 self.skip_ws();
453
454 self.builder.start_node(CONSTRAINT.into());
455
456 while self.current() == Some(L_ANGLE)
457 || self.current() == Some(R_ANGLE)
458 || self.current() == Some(EQUAL)
459 || self.current() == Some(NOT)
460 {
461 self.bump();
462 }
463
464 self.builder.finish_node();
465
466 self.skip_ws();
467
468 if self.current() == Some(IDENT) {
469 self.bump();
470 } else {
471 self.error("Expected version".to_string());
472 }
473
474 if self.current() == Some(R_PARENS) {
475 self.bump();
476 } else {
477 self.error("Expected ')'".to_string());
478 }
479
480 self.builder.finish_node();
481 }
482
483 self.builder.finish_node();
484 }
485
486 fn parse(mut self) -> Parse {
487 self.builder.start_node(SyntaxKind::ROOT.into());
488
489 self.skip_ws();
490
491 while self.current().is_some() {
492 match self.current() {
493 Some(IDENT) => self.parse_relation(),
494 Some(COMMA) => {
495 }
497 Some(c) => {
498 self.error(format!("expected identifier or comma but got {c:?}"));
499 }
500 None => {
501 self.error("expected identifier but got end of file".to_string());
502 }
503 }
504
505 self.skip_ws();
506 match self.current() {
507 Some(COMMA) => {
508 self.bump();
509 }
510 None => {
511 break;
512 }
513 c => {
514 self.error(format!("expected comma or end of file but got {c:?}"));
515 }
516 }
517 self.skip_ws();
518 }
519
520 self.builder.finish_node();
521 Parse {
523 green_node: self.builder.finish(),
524 errors: self.errors,
525 }
526 }
527 fn bump(&mut self) {
529 let (kind, text) = self.tokens.pop().unwrap();
530 self.builder.token(kind.into(), text.as_str());
531 }
532 fn current(&self) -> Option<SyntaxKind> {
534 self.tokens.last().map(|(kind, _)| *kind)
535 }
536 fn skip_ws(&mut self) {
537 while self.current() == Some(WHITESPACE) || self.current() == Some(NEWLINE) {
538 self.bump()
539 }
540 }
541
542 fn peek_past_ws(&self) -> Option<SyntaxKind> {
543 let mut i = self.tokens.len();
544 while i > 0 {
545 i -= 1;
546 match self.tokens[i].0 {
547 WHITESPACE | NEWLINE => {}
548 _ => return Some(self.tokens[i].0),
549 }
550 }
551 None
552 }
553 }
554
555 let mut tokens = crate::relations::lex(text);
556 tokens.reverse();
557 Parser {
558 tokens,
559 builder: GreenNodeBuilder::new(),
560 errors: Vec::new(),
561 }
562 .parse()
563 }
564
565 type SyntaxNode = rowan::SyntaxNode<Lang>;
571 #[allow(unused)]
572 type SyntaxToken = rowan::SyntaxToken<Lang>;
573 #[allow(unused)]
574 type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;
575
576 impl Parse {
577 fn root_mut(&self) -> Relations {
578 Relations::cast(SyntaxNode::new_root_mut(self.green_node.clone())).unwrap()
579 }
580 }
581
582 macro_rules! ast_node {
583 ($ast:ident, $kind:ident) => {
584 #[repr(transparent)]
586 pub struct $ast(SyntaxNode);
587 impl $ast {
588 #[allow(unused)]
589 fn cast(node: SyntaxNode) -> Option<Self> {
590 if node.kind() == $kind {
591 Some(Self(node))
592 } else {
593 None
594 }
595 }
596 }
597
598 impl std::fmt::Display for $ast {
599 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
600 f.write_str(&self.0.text().to_string())
601 }
602 }
603 };
604 }
605
606 ast_node!(Relations, ROOT);
607 ast_node!(Relation, RELATION);
608
609 impl PartialEq for Relations {
610 fn eq(&self, other: &Self) -> bool {
611 self.relations().collect::<Vec<_>>() == other.relations().collect::<Vec<_>>()
612 }
613 }
614
615 impl PartialEq for Relation {
616 fn eq(&self, other: &Self) -> bool {
617 self.name() == other.name() && self.version() == other.version()
618 }
619 }
620
621 #[cfg(feature = "serde")]
622 impl serde::Serialize for Relations {
623 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
624 let rep = self.to_string();
625 serializer.serialize_str(&rep)
626 }
627 }
628
629 #[cfg(feature = "serde")]
630 impl<'de> serde::Deserialize<'de> for Relations {
631 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
632 let s = String::deserialize(deserializer)?;
633 let relations = s.parse().map_err(serde::de::Error::custom)?;
634 Ok(relations)
635 }
636 }
637
638 impl std::fmt::Debug for Relations {
639 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
640 let mut s = f.debug_struct("Relations");
641
642 for relation in self.relations() {
643 s.field("relation", &relation);
644 }
645
646 s.finish()
647 }
648 }
649
650 impl std::fmt::Debug for Relation {
651 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
652 let mut s = f.debug_struct("Relation");
653
654 s.field("name", &self.name());
655
656 if let Some((vc, version)) = self.version() {
657 s.field("version", &vc);
658 s.field("version", &version);
659 }
660
661 s.finish()
662 }
663 }
664
665 #[cfg(feature = "serde")]
666 impl serde::Serialize for Relation {
667 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
668 let rep = self.to_string();
669 serializer.serialize_str(&rep)
670 }
671 }
672
673 #[cfg(feature = "serde")]
674 impl<'de> serde::Deserialize<'de> for Relation {
675 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
676 let s = String::deserialize(deserializer)?;
677 let relation = s.parse().map_err(serde::de::Error::custom)?;
678 Ok(relation)
679 }
680 }
681
682 impl Default for Relations {
683 fn default() -> Self {
684 Self::new()
685 }
686 }
687
688 impl Relations {
689 pub fn new() -> Self {
691 Self::from(vec![])
692 }
693
694 #[must_use]
696 pub fn wrap_and_sort(self) -> Self {
697 let mut entries = self
698 .relations()
699 .map(|e| e.wrap_and_sort())
700 .collect::<Vec<_>>();
701 entries.sort();
702 Self::from(entries)
704 }
705
706 pub fn relations(&self) -> impl Iterator<Item = Relation> + '_ {
708 self.0.children().filter_map(Relation::cast)
709 }
710
711 pub fn iter(&self) -> impl Iterator<Item = Relation> + '_ {
713 self.relations()
714 }
715
716 pub fn get_relation(&self, idx: usize) -> Option<Relation> {
718 self.relations().nth(idx)
719 }
720
721 pub fn remove_relation(&mut self, idx: usize) -> Relation {
723 let mut relation = self.get_relation(idx).unwrap();
724 relation.remove();
725 relation
726 }
727
728 pub fn insert(&mut self, idx: usize, relation: Relation) {
730 let is_empty = !self.0.children_with_tokens().any(|n| n.kind() == COMMA);
731 let (position, new_children) = if let Some(current_relation) = self.relations().nth(idx)
732 {
733 let to_insert: Vec<NodeOrToken<GreenNode, GreenToken>> = if idx == 0 && is_empty {
734 vec![relation.0.green().into()]
735 } else {
736 vec![
737 relation.0.green().into(),
738 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
739 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
740 ]
741 };
742
743 (current_relation.0.index(), to_insert)
744 } else {
745 let child_count = self.0.children_with_tokens().count();
746 (
747 child_count,
748 if idx == 0 {
749 vec![relation.0.green().into()]
750 } else {
751 vec![
752 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
753 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
754 relation.0.green().into(),
755 ]
756 },
757 )
758 };
759 self.0 = SyntaxNode::new_root_mut(
761 self.0.replace_with(
762 self.0
763 .green()
764 .splice_children(position..position, new_children),
765 ),
766 );
767 }
768
769 pub fn replace(&mut self, idx: usize, relation: Relation) {
771 let current_relation = self.get_relation(idx).unwrap();
772 self.0.splice_children(
773 current_relation.0.index()..current_relation.0.index() + 1,
774 vec![relation.0.into()],
775 );
776 }
777
778 pub fn push(&mut self, relation: Relation) {
780 let pos = self.relations().count();
781 self.insert(pos, relation);
782 }
783
784 pub fn parse_relaxed(s: &str) -> (Relations, Vec<String>) {
786 let parse = parse(s);
787 (parse.root_mut(), parse.errors)
788 }
789
790 pub fn satisfied_by(
792 &self,
793 package_version: impl crate::relations::VersionLookup + Copy,
794 ) -> bool {
795 self.relations().all(|e| e.satisfied_by(package_version))
796 }
797
798 pub fn is_empty(&self) -> bool {
800 self.relations().count() == 0
801 }
802
803 pub fn len(&self) -> usize {
805 self.relations().count()
806 }
807 }
808
809 impl From<Vec<Relation>> for Relations {
810 fn from(entries: Vec<Relation>) -> Self {
811 let mut builder = GreenNodeBuilder::new();
812 builder.start_node(ROOT.into());
813 for (i, relation) in entries.into_iter().enumerate() {
814 if i > 0 {
815 builder.token(COMMA.into(), ",");
816 builder.token(WHITESPACE.into(), " ");
817 }
818 inject(&mut builder, relation.0);
819 }
820 builder.finish_node();
821 Relations(SyntaxNode::new_root_mut(builder.finish()))
822 }
823 }
824
825 impl From<Relation> for Relations {
826 fn from(relation: Relation) -> Self {
827 Self::from(vec![relation])
828 }
829 }
830
831 impl From<Relation> for crate::lossy::Relation {
832 fn from(relation: Relation) -> Self {
833 let mut rel = crate::lossy::Relation::new();
834 rel.name = relation.name();
835 rel.version = relation.version();
836 rel
837 }
838 }
839
840 impl From<Relations> for crate::lossy::Relations {
841 fn from(relations: Relations) -> Self {
842 let mut rels = crate::lossy::Relations::new();
843 for relation in relations.relations() {
844 rels.0.push(relation.into());
845 }
846 rels
847 }
848 }
849
850 impl From<crate::lossy::Relations> for Relations {
851 fn from(relations: crate::lossy::Relations) -> Self {
852 let mut entries = vec![];
853 for relation in relations.iter() {
854 entries.push(relation.clone().into());
855 }
856 Self::from(entries)
857 }
858 }
859
860 impl From<crate::lossy::Relation> for Relation {
861 fn from(relation: crate::lossy::Relation) -> Self {
862 Relation::new(&relation.name, relation.version)
863 }
864 }
865
866 fn inject(builder: &mut GreenNodeBuilder, node: SyntaxNode) {
867 builder.start_node(node.kind().into());
868 for child in node.children_with_tokens() {
869 match child {
870 rowan::NodeOrToken::Node(child) => {
871 inject(builder, child);
872 }
873 rowan::NodeOrToken::Token(token) => {
874 builder.token(token.kind().into(), token.text());
875 }
876 }
877 }
878 builder.finish_node();
879 }
880
881 impl Relation {
882 pub fn new(name: &str, version_constraint: Option<(VersionConstraint, Version)>) -> Self {
896 let mut builder = GreenNodeBuilder::new();
897 builder.start_node(SyntaxKind::RELATION.into());
898 builder.token(IDENT.into(), name);
899 if let Some((vc, version)) = version_constraint {
900 builder.token(WHITESPACE.into(), " ");
901 builder.start_node(SyntaxKind::VERSION.into());
902 builder.token(L_PARENS.into(), "(");
903 builder.start_node(SyntaxKind::CONSTRAINT.into());
904 push_constraint_tokens(&mut builder, &vc);
905 builder.finish_node();
906
907 builder.token(WHITESPACE.into(), " ");
908
909 builder.token(IDENT.into(), version.to_string().as_str());
910
911 builder.token(R_PARENS.into(), ")");
912
913 builder.finish_node();
914 }
915
916 builder.finish_node();
917 Relation(SyntaxNode::new_root_mut(builder.finish()))
918 }
919
920 #[must_use]
929 pub fn wrap_and_sort(&self) -> Self {
930 let mut builder = GreenNodeBuilder::new();
931 builder.start_node(SyntaxKind::RELATION.into());
932 builder.token(IDENT.into(), self.name().as_str());
933 if let Some((vc, version)) = self.version() {
934 builder.token(WHITESPACE.into(), " ");
935 builder.start_node(SyntaxKind::VERSION.into());
936 builder.token(L_PARENS.into(), "(");
937 builder.start_node(SyntaxKind::CONSTRAINT.into());
938 push_constraint_tokens(&mut builder, &vc);
939 builder.finish_node();
940 builder.token(WHITESPACE.into(), " ");
941 builder.token(IDENT.into(), version.to_string().as_str());
942 builder.token(R_PARENS.into(), ")");
943 builder.finish_node();
944 }
945 builder.finish_node();
946 Relation(SyntaxNode::new_root_mut(builder.finish()))
947 }
948
949 pub fn simple(name: &str) -> Self {
958 Self::new(name, None)
959 }
960
961 pub fn drop_constraint(&mut self) -> bool {
972 let version_token = self.0.children().find(|n| n.kind() == VERSION);
973 if let Some(version_token) = version_token {
974 while let Some(prev) = version_token.prev_sibling_or_token() {
976 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
977 prev.detach();
978 } else {
979 break;
980 }
981 }
982 version_token.detach();
983 return true;
984 }
985
986 false
987 }
988
989 pub fn name(&self) -> String {
998 self.0
999 .children_with_tokens()
1000 .find_map(|it| match it {
1001 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
1002 _ => None,
1003 })
1004 .unwrap()
1005 .text()
1006 .to_string()
1007 }
1008
1009 pub fn version(&self) -> Option<(VersionConstraint, Version)> {
1011 let vc = self.0.children().find(|n| n.kind() == VERSION);
1012 let vc = vc.as_ref()?;
1013 let constraint = vc.children().find(|n| n.kind() == CONSTRAINT);
1014
1015 let version = vc.children_with_tokens().find_map(|it| match it {
1016 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
1017 _ => None,
1018 });
1019
1020 if let (Some(constraint), Some(version)) = (constraint, version) {
1021 let vc: VersionConstraint = constraint.to_string().parse().unwrap();
1022 Some((vc, (version.text().to_string()).parse().unwrap()))
1023 } else {
1024 None
1025 }
1026 }
1027
1028 pub fn set_version(&mut self, version_constraint: Option<(VersionConstraint, Version)>) {
1039 let current_version = self.0.children().find(|n| n.kind() == VERSION);
1040 if let Some((vc, version)) = version_constraint {
1041 let mut builder = GreenNodeBuilder::new();
1042 builder.start_node(VERSION.into());
1043 builder.token(L_PARENS.into(), "(");
1044 builder.start_node(CONSTRAINT.into());
1045 push_constraint_tokens(&mut builder, &vc);
1046 builder.finish_node(); builder.token(WHITESPACE.into(), " ");
1048 builder.token(IDENT.into(), version.to_string().as_str());
1049 builder.token(R_PARENS.into(), ")");
1050 builder.finish_node(); if let Some(current_version) = current_version {
1053 self.0.splice_children(
1054 current_version.index()..current_version.index() + 1,
1055 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
1056 );
1057 } else {
1058 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
1059 let idx = if let Some(name_node) = name_node {
1060 name_node.index() + 1
1061 } else {
1062 0
1063 };
1064 let new_children = vec![
1065 GreenToken::new(WHITESPACE.into(), " ").into(),
1066 builder.finish().into(),
1067 ];
1068 let new_root = SyntaxNode::new_root_mut(
1069 self.0.green().splice_children(idx..idx, new_children),
1070 );
1071 if let Some(parent) = self.0.parent() {
1072 parent.splice_children(
1073 self.0.index()..self.0.index() + 1,
1074 vec![new_root.into()],
1075 );
1076 self.0 = parent
1077 .children_with_tokens()
1078 .nth(self.0.index())
1079 .unwrap()
1080 .clone()
1081 .into_node()
1082 .unwrap();
1083 } else {
1084 self.0 = new_root;
1085 }
1086 }
1087 } else if let Some(current_version) = current_version {
1088 while let Some(prev) = current_version.prev_sibling_or_token() {
1090 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
1091 prev.detach();
1092 } else {
1093 break;
1094 }
1095 }
1096 current_version.detach();
1097 }
1098 }
1099
1100 pub fn remove(&mut self) {
1112 let is_first = !self
1113 .0
1114 .siblings(Direction::Prev)
1115 .skip(1)
1116 .any(|n| n.kind() == RELATION);
1117 if !is_first {
1118 while let Some(n) = self.0.prev_sibling_or_token() {
1121 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1122 n.detach();
1123 } else if n.kind() == COMMA {
1124 n.detach();
1125 break;
1126 } else {
1127 break;
1128 }
1129 }
1130 while let Some(n) = self.0.prev_sibling_or_token() {
1131 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1132 n.detach();
1133 } else {
1134 break;
1135 }
1136 }
1137 } else {
1138 while let Some(n) = self.0.next_sibling_or_token() {
1141 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1142 n.detach();
1143 } else if n.kind() == COMMA {
1144 n.detach();
1145 break;
1146 } else {
1147 panic!("Unexpected node: {n:?}");
1148 }
1149 }
1150
1151 while let Some(n) = self.0.next_sibling_or_token() {
1152 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1153 n.detach();
1154 } else {
1155 break;
1156 }
1157 }
1158 }
1159 self.0.detach();
1160 }
1161
1162 pub fn satisfied_by(
1164 &self,
1165 package_version: impl crate::relations::VersionLookup + Copy,
1166 ) -> bool {
1167 let name = self.name();
1168 let version = self.version();
1169 if let Some(version) = version {
1170 if let Some(package_version) = package_version.lookup_version(&name) {
1171 match version.0 {
1172 VersionConstraint::GreaterThanEqual => {
1173 package_version.into_owned() >= version.1
1174 }
1175 VersionConstraint::LessThanEqual => {
1176 package_version.into_owned() <= version.1
1177 }
1178 VersionConstraint::Equal => package_version.into_owned() == version.1,
1179 VersionConstraint::NotEqual => package_version.into_owned() != version.1,
1180 VersionConstraint::GreaterThan => package_version.into_owned() > version.1,
1181 VersionConstraint::LessThan => package_version.into_owned() < version.1,
1182 }
1183 } else {
1184 false
1185 }
1186 } else {
1187 true
1188 }
1189 }
1190 }
1191
1192 impl PartialOrd for Relation {
1193 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1194 Some(self.cmp(other))
1195 }
1196 }
1197
1198 impl Eq for Relation {}
1199
1200 impl Ord for Relation {
1201 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1202 let name_cmp = self.name().cmp(&other.name());
1204 if name_cmp != std::cmp::Ordering::Equal {
1205 return name_cmp;
1206 }
1207
1208 let self_version = self.version();
1209 let other_version = other.version();
1210
1211 match (self_version, other_version) {
1212 (Some((self_vc, self_version)), Some((other_vc, other_version))) => {
1213 let vc_cmp = self_vc.cmp(&other_vc);
1214 if vc_cmp != std::cmp::Ordering::Equal {
1215 return vc_cmp;
1216 }
1217
1218 self_version.cmp(&other_version)
1219 }
1220 (Some(_), None) => std::cmp::Ordering::Greater,
1221 (None, Some(_)) => std::cmp::Ordering::Less,
1222 (None, None) => std::cmp::Ordering::Equal,
1223 }
1224 }
1225 }
1226
1227 impl std::str::FromStr for Relations {
1228 type Err = String;
1229
1230 fn from_str(s: &str) -> Result<Self, Self::Err> {
1231 let parse = parse(s);
1232 if parse.errors.is_empty() {
1233 Ok(parse.root_mut())
1234 } else {
1235 Err(parse.errors.join("\n"))
1236 }
1237 }
1238 }
1239
1240 impl std::str::FromStr for Relation {
1241 type Err = String;
1242
1243 fn from_str(s: &str) -> Result<Self, Self::Err> {
1244 let rels = s.parse::<Relations>()?;
1245 let mut relations = rels.relations();
1246
1247 let relation = if let Some(relation) = relations.next() {
1248 relation
1249 } else {
1250 return Err("No relation found".to_string());
1251 };
1252
1253 if relations.next().is_some() {
1254 return Err("Multiple relations found".to_string());
1255 }
1256
1257 Ok(relation)
1258 }
1259 }
1260
1261 #[cfg(test)]
1262 mod tests {
1263 use super::*;
1264
1265 #[test]
1266 fn test_parse() {
1267 let input = "cli";
1268 let parsed: Relations = input.parse().unwrap();
1269 assert_eq!(parsed.to_string(), input);
1270 assert_eq!(parsed.relations().count(), 1);
1271 let relation = parsed.relations().next().unwrap();
1272 assert_eq!(relation.to_string(), "cli");
1273 assert_eq!(relation.version(), None);
1274
1275 let input = "cli (>= 0.20.21)";
1276 let parsed: Relations = input.parse().unwrap();
1277 assert_eq!(parsed.to_string(), input);
1278 assert_eq!(parsed.relations().count(), 1);
1279 let relation = parsed.relations().next().unwrap();
1280 assert_eq!(relation.to_string(), "cli (>= 0.20.21)");
1281 assert_eq!(
1282 relation.version(),
1283 Some((
1284 VersionConstraint::GreaterThanEqual,
1285 "0.20.21".parse().unwrap()
1286 ))
1287 );
1288
1289 let input = "xml2 (> 1.0.0)";
1290 let parsed: Relations = input.parse().unwrap();
1291 assert_eq!(parsed.to_string(), input);
1292 let relation = parsed.relations().next().unwrap();
1293 assert_eq!(
1294 relation.version(),
1295 Some((VersionConstraint::GreaterThan, "1.0.0".parse().unwrap()))
1296 );
1297
1298 let input = "xml2 (< 2.0.0)";
1299 let parsed: Relations = input.parse().unwrap();
1300 assert_eq!(parsed.to_string(), input);
1301 let relation = parsed.relations().next().unwrap();
1302 assert_eq!(
1303 relation.version(),
1304 Some((VersionConstraint::LessThan, "2.0.0".parse().unwrap()))
1305 );
1306 }
1307
1308 #[test]
1309 fn test_r_equality_operators() {
1310 let input = "xml2 (== 1.0.0)";
1311 let parsed: Relations = input.parse().unwrap();
1312 assert_eq!(parsed.to_string(), input);
1313 let relation = parsed.relations().next().unwrap();
1314 assert_eq!(
1315 relation.version(),
1316 Some((VersionConstraint::Equal, "1.0.0".parse().unwrap()))
1317 );
1318
1319 let input = "xml2 (!= 1.0.0)";
1320 let parsed: Relations = input.parse().unwrap();
1321 assert_eq!(parsed.to_string(), input);
1322 let relation = parsed.relations().next().unwrap();
1323 assert_eq!(
1324 relation.version(),
1325 Some((VersionConstraint::NotEqual, "1.0.0".parse().unwrap()))
1326 );
1327 }
1328
1329 #[test]
1330 fn test_constructed_relations_use_r_operators() {
1331 let cases = [
1332 (VersionConstraint::LessThan, "xml2 (< 1.0)"),
1333 (VersionConstraint::LessThanEqual, "xml2 (<= 1.0)"),
1334 (VersionConstraint::Equal, "xml2 (== 1.0)"),
1335 (VersionConstraint::NotEqual, "xml2 (!= 1.0)"),
1336 (VersionConstraint::GreaterThan, "xml2 (> 1.0)"),
1337 (VersionConstraint::GreaterThanEqual, "xml2 (>= 1.0)"),
1338 ];
1339 for (vc, expected) in cases {
1340 let relation = Relation::new("xml2", Some((vc, "1.0".parse().unwrap())));
1341 assert_eq!(relation.to_string(), expected);
1342 }
1343 }
1344
1345 #[test]
1346 fn test_multiple() {
1347 let input = "cli (>= 0.20.21), cli (< 0.21)";
1348 let parsed: Relations = input.parse().unwrap();
1349 assert_eq!(parsed.to_string(), input);
1350 assert_eq!(parsed.relations().count(), 2);
1351 let relation = parsed.relations().next().unwrap();
1352 assert_eq!(relation.to_string(), "cli (>= 0.20.21)");
1353 assert_eq!(
1354 relation.version(),
1355 Some((
1356 VersionConstraint::GreaterThanEqual,
1357 "0.20.21".parse().unwrap()
1358 ))
1359 );
1360 let relation = parsed.relations().nth(1).unwrap();
1361 assert_eq!(relation.to_string(), "cli (< 0.21)");
1362 assert_eq!(
1363 relation.version(),
1364 Some((VersionConstraint::LessThan, "0.21".parse().unwrap()))
1365 );
1366 }
1367
1368 #[test]
1369 fn test_new() {
1370 let r = Relation::new(
1371 "cli",
1372 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
1373 );
1374
1375 assert_eq!(r.to_string(), "cli (>= 2.0)");
1376 }
1377
1378 #[test]
1379 fn test_drop_constraint() {
1380 let mut r = Relation::new(
1381 "cli",
1382 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
1383 );
1384
1385 r.drop_constraint();
1386
1387 assert_eq!(r.to_string(), "cli");
1388 }
1389
1390 #[test]
1391 fn test_simple() {
1392 let r = Relation::simple("cli");
1393
1394 assert_eq!(r.to_string(), "cli");
1395 }
1396
1397 #[test]
1398 fn test_remove_first_relation() {
1399 let mut rels: Relations = r#"cli (>= 0.20.21), cli (< 0.21)"#.parse().unwrap();
1400 let removed = rels.remove_relation(0);
1401 assert_eq!(removed.to_string(), "cli (>= 0.20.21)");
1402 assert_eq!(rels.to_string(), "cli (< 0.21)");
1403 }
1404
1405 #[test]
1406 fn test_remove_last_relation() {
1407 let mut rels: Relations = r#"cli (>= 0.20.21), cli (< 0.21)"#.parse().unwrap();
1408 rels.remove_relation(1);
1409 assert_eq!(rels.to_string(), "cli (>= 0.20.21)");
1410 }
1411
1412 #[test]
1413 fn test_remove_middle() {
1414 let mut rels: Relations =
1415 r#"cli (>= 0.20.21), cli (< 0.21), cli (< 0.22)"#.parse().unwrap();
1416 rels.remove_relation(1);
1417 assert_eq!(rels.to_string(), "cli (>= 0.20.21), cli (< 0.22)");
1418 }
1419
1420 #[test]
1421 fn test_remove_added() {
1422 let mut rels: Relations = r#"cli (>= 0.20.21)"#.parse().unwrap();
1423 let relation = Relation::simple("cli");
1424 rels.push(relation);
1425 rels.remove_relation(1);
1426 assert_eq!(rels.to_string(), "cli (>= 0.20.21)");
1427 }
1428
1429 #[test]
1430 fn test_push() {
1431 let mut rels: Relations = r#"cli (>= 0.20.21)"#.parse().unwrap();
1432 let relation = Relation::simple("cli");
1433 rels.push(relation);
1434 assert_eq!(rels.to_string(), "cli (>= 0.20.21), cli");
1435 }
1436
1437 #[test]
1438 fn test_push_from_empty() {
1439 let mut rels: Relations = "".parse().unwrap();
1440 let relation = Relation::simple("cli");
1441 rels.push(relation);
1442 assert_eq!(rels.to_string(), "cli");
1443 }
1444
1445 #[test]
1446 fn test_insert() {
1447 let mut rels: Relations = r#"cli (>= 0.20.21), cli (< 0.21)"#.parse().unwrap();
1448 let relation = Relation::simple("cli");
1449 rels.insert(1, relation);
1450 assert_eq!(rels.to_string(), "cli (>= 0.20.21), cli, cli (< 0.21)");
1451 }
1452
1453 #[test]
1454 fn test_insert_at_start() {
1455 let mut rels: Relations = r#"cli (>= 0.20.21), cli (< 0.21)"#.parse().unwrap();
1456 let relation = Relation::simple("cli");
1457 rels.insert(0, relation);
1458 assert_eq!(rels.to_string(), "cli, cli (>= 0.20.21), cli (< 0.21)");
1459 }
1460
1461 #[test]
1462 fn test_insert_after_error() {
1463 let (mut rels, errors) = Relations::parse_relaxed("@foo@, debhelper (>= 1.0)");
1464 assert_eq!(
1465 errors,
1466 vec![
1467 "expected identifier or comma but got ERROR",
1468 "expected comma or end of file but got Some(IDENT)",
1469 "expected identifier or comma but got ERROR"
1470 ]
1471 );
1472 let relation = Relation::simple("bar");
1473 rels.push(relation);
1474 assert_eq!(rels.to_string(), "@foo@, debhelper (>= 1.0), bar");
1475 }
1476
1477 #[test]
1478 fn test_insert_before_error() {
1479 let (mut rels, errors) = Relations::parse_relaxed("debhelper (>= 1.0), @foo@, bla");
1480 assert_eq!(
1481 errors,
1482 vec![
1483 "expected identifier or comma but got ERROR",
1484 "expected comma or end of file but got Some(IDENT)",
1485 "expected identifier or comma but got ERROR"
1486 ]
1487 );
1488 let relation = Relation::simple("bar");
1489 rels.insert(0, relation);
1490 assert_eq!(rels.to_string(), "bar, debhelper (>= 1.0), @foo@, bla");
1491 }
1492
1493 #[test]
1494 fn test_replace() {
1495 let mut rels: Relations = r#"cli (>= 0.20.21), cli (< 0.21)"#.parse().unwrap();
1496 let relation = Relation::simple("cli");
1497 rels.replace(1, relation);
1498 assert_eq!(rels.to_string(), "cli (>= 0.20.21), cli");
1499 }
1500
1501 #[test]
1502 fn test_parse_relation() {
1503 let parsed: Relation = "cli (>= 0.20.21)".parse().unwrap();
1504 assert_eq!(parsed.to_string(), "cli (>= 0.20.21)");
1505 assert_eq!(
1506 parsed.version(),
1507 Some((
1508 VersionConstraint::GreaterThanEqual,
1509 "0.20.21".parse().unwrap()
1510 ))
1511 );
1512 assert_eq!(
1513 "foo, bar".parse::<Relation>().unwrap_err(),
1514 "Multiple relations found"
1515 );
1516 assert_eq!("".parse::<Relation>().unwrap_err(), "No relation found");
1517 }
1518
1519 #[test]
1520 fn test_relations_satisfied_by() {
1521 let rels: Relations = "cli (>= 0.20.21), cli (< 0.21)".parse().unwrap();
1522 let satisfied = |name: &str| -> Option<Version> {
1523 match name {
1524 "cli" => Some("0.20.21".parse().unwrap()),
1525 _ => None,
1526 }
1527 };
1528 assert!(rels.satisfied_by(satisfied));
1529
1530 let satisfied = |name: &str| match name {
1531 "cli" => Some("0.21".parse().unwrap()),
1532 _ => None,
1533 };
1534 assert!(!rels.satisfied_by(satisfied));
1535
1536 let satisfied = |name: &str| match name {
1537 "cli" => Some("0.20.20".parse().unwrap()),
1538 _ => None,
1539 };
1540 assert!(!rels.satisfied_by(satisfied));
1541 }
1542
1543 #[test]
1544 fn test_wrap_and_sort_relation() {
1545 let relation: Relation = " cli (>= 11.0)".parse().unwrap();
1546
1547 let wrapped = relation.wrap_and_sort();
1548
1549 assert_eq!(wrapped.to_string(), "cli (>= 11.0)");
1550 }
1551
1552 #[test]
1553 fn test_wrap_and_sort_relations() {
1554 let relations: Relations = "cli (>= 0.20.21) , \n\n\n\ncli (< 0.21)".parse().unwrap();
1555
1556 let wrapped = relations.wrap_and_sort();
1557
1558 assert_eq!(wrapped.to_string(), "cli (< 0.21), cli (>= 0.20.21)");
1559 }
1560
1561 #[cfg(feature = "serde")]
1562 #[test]
1563 fn test_serialize_relations() {
1564 let relations: Relations = "cli (>= 0.20.21), cli (< 0.21)".parse().unwrap();
1565 let serialized = serde_json::to_string(&relations).unwrap();
1566 assert_eq!(serialized, r#""cli (>= 0.20.21), cli (< 0.21)""#);
1567 }
1568
1569 #[cfg(feature = "serde")]
1570 #[test]
1571 fn test_deserialize_relations() {
1572 let relations: Relations = "cli (>= 0.20.21), cli (< 0.21)".parse().unwrap();
1573 let serialized = serde_json::to_string(&relations).unwrap();
1574 let deserialized: Relations = serde_json::from_str(&serialized).unwrap();
1575 assert_eq!(deserialized.to_string(), relations.to_string());
1576 }
1577
1578 #[cfg(feature = "serde")]
1579 #[test]
1580 fn test_serialize_relation() {
1581 let relation: Relation = "cli (>= 0.20.21)".parse().unwrap();
1582 let serialized = serde_json::to_string(&relation).unwrap();
1583 assert_eq!(serialized, r#""cli (>= 0.20.21)""#);
1584 }
1585
1586 #[cfg(feature = "serde")]
1587 #[test]
1588 fn test_deserialize_relation() {
1589 let relation: Relation = "cli (>= 0.20.21)".parse().unwrap();
1590 let serialized = serde_json::to_string(&relation).unwrap();
1591 let deserialized: Relation = serde_json::from_str(&serialized).unwrap();
1592 assert_eq!(deserialized.to_string(), relation.to_string());
1593 }
1594
1595 #[test]
1596 fn test_relation_set_version() {
1597 let mut rel: Relation = "vign".parse().unwrap();
1598 rel.set_version(None);
1599 assert_eq!("vign", rel.to_string());
1600 rel.set_version(Some((
1601 VersionConstraint::GreaterThanEqual,
1602 "2.0".parse().unwrap(),
1603 )));
1604 assert_eq!("vign (>= 2.0)", rel.to_string());
1605 rel.set_version(None);
1606 assert_eq!("vign", rel.to_string());
1607 rel.set_version(Some((
1608 VersionConstraint::GreaterThanEqual,
1609 "2.0".parse().unwrap(),
1610 )));
1611 rel.set_version(Some((
1612 VersionConstraint::GreaterThanEqual,
1613 "1.1".parse().unwrap(),
1614 )));
1615 assert_eq!("vign (>= 1.1)", rel.to_string());
1616 }
1617
1618 #[test]
1619 fn test_wrap_and_sort_removes_empty_entries() {
1620 let relations: Relations = "foo, , bar, ".parse().unwrap();
1621 let wrapped = relations.wrap_and_sort();
1622 assert_eq!(wrapped.to_string(), "bar, foo");
1623 }
1624 }
1625}
1626
1627#[cfg(test)]
1628mod tests {
1629 use super::*;
1630
1631 #[test]
1632 fn test_parse() {
1633 let s = r###"Package: mypackage
1634Title: What the Package Does (One Line, Title Case)
1635Version: 0.0.0.9000
1636Authors@R:
1637 person("First", "Last", , "first.last@example.com", role = c("aut", "cre"),
1638 comment = c(ORCID = "YOUR-ORCID-ID"))
1639Description: What the package does (one paragraph).
1640License: `use_mit_license()`, `use_gpl3_license()` or friends to pick a
1641 license
1642Encoding: UTF-8
1643Roxygen: list(markdown = TRUE)
1644RoxygenNote: 7.3.2
1645"###;
1646 let desc: RDescription = s.parse().unwrap();
1647
1648 assert_eq!(desc.package(), Some("mypackage".to_string()));
1649 assert_eq!(
1650 desc.title(),
1651 Some("What the Package Does (One Line, Title Case)".to_string())
1652 );
1653 assert_eq!(desc.version(), Some("0.0.0.9000".to_string()));
1654 assert_eq!(
1655 desc.authors(),
1656 Some(RCode(
1657 r#"person("First", "Last", , "first.last@example.com", role = c("aut", "cre"),
1658comment = c(ORCID = "YOUR-ORCID-ID"))"#
1659 .to_string()
1660 ))
1661 );
1662 assert_eq!(
1663 desc.description(),
1664 Some("What the package does (one paragraph).".to_string())
1665 );
1666 assert_eq!(
1667 desc.license(),
1668 Some(
1669 "`use_mit_license()`, `use_gpl3_license()` or friends to pick a\nlicense"
1670 .to_string()
1671 )
1672 );
1673 assert_eq!(desc.encoding(), Some("UTF-8".to_string()));
1674 assert_eq!(desc.roxygen(), Some("list(markdown = TRUE)".to_string()));
1675 assert_eq!(desc.roxygen_note(), Some("7.3.2".to_string()));
1676
1677 assert_eq!(desc.to_string(), s);
1678 }
1679
1680 #[test]
1681 fn test_parse_dplyr() {
1682 let s = include_str!("../testdata/dplyr.desc");
1683
1684 let desc: RDescription = s.parse().unwrap();
1685 assert_eq!("dplyr", desc.package().unwrap());
1686 assert_eq!(
1687 "https://dplyr.tidyverse.org, https://github.com/tidyverse/dplyr",
1688 desc.url().unwrap().as_str()
1689 );
1690 }
1691}