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 struct Parse {
385 green_node: GreenNode,
386 #[allow(unused)]
387 errors: Vec<String>,
388 }
389
390 fn parse(text: &str) -> Parse {
391 struct Parser {
392 tokens: Vec<(SyntaxKind, String)>,
395 builder: GreenNodeBuilder<'static>,
397 errors: Vec<String>,
400 }
401
402 impl Parser {
403 fn error(&mut self, error: String) {
404 self.errors.push(error);
405 self.builder.start_node(SyntaxKind::ERROR.into());
406 if self.current().is_some() {
407 self.bump();
408 }
409 self.builder.finish_node();
410 }
411
412 fn parse_relation(&mut self) {
413 self.builder.start_node(SyntaxKind::RELATION.into());
414 if self.current() == Some(IDENT) {
415 self.bump();
416 } else {
417 self.error("Expected package name".to_string());
418 }
419 match self.peek_past_ws() {
420 Some(COMMA) => {}
421 None | Some(L_PARENS) => {
422 self.skip_ws();
423 }
424 e => {
425 self.skip_ws();
426 self.error(format!(
427 "Expected ':' or '|' or '[' or '<' or ',' but got {e:?}"
428 ));
429 }
430 }
431
432 if self.peek_past_ws() == Some(L_PARENS) {
433 self.skip_ws();
434 self.builder.start_node(VERSION.into());
435 self.bump();
436 self.skip_ws();
437
438 self.builder.start_node(CONSTRAINT.into());
439
440 while self.current() == Some(L_ANGLE)
441 || self.current() == Some(R_ANGLE)
442 || self.current() == Some(EQUAL)
443 {
444 self.bump();
445 }
446
447 self.builder.finish_node();
448
449 self.skip_ws();
450
451 if self.current() == Some(IDENT) {
452 self.bump();
453 } else {
454 self.error("Expected version".to_string());
455 }
456
457 if self.current() == Some(R_PARENS) {
458 self.bump();
459 } else {
460 self.error("Expected ')'".to_string());
461 }
462
463 self.builder.finish_node();
464 }
465
466 self.builder.finish_node();
467 }
468
469 fn parse(mut self) -> Parse {
470 self.builder.start_node(SyntaxKind::ROOT.into());
471
472 self.skip_ws();
473
474 while self.current().is_some() {
475 match self.current() {
476 Some(IDENT) => self.parse_relation(),
477 Some(COMMA) => {
478 }
480 Some(c) => {
481 self.error(format!("expected identifier or comma but got {c:?}"));
482 }
483 None => {
484 self.error("expected identifier but got end of file".to_string());
485 }
486 }
487
488 self.skip_ws();
489 match self.current() {
490 Some(COMMA) => {
491 self.bump();
492 }
493 None => {
494 break;
495 }
496 c => {
497 self.error(format!("expected comma or end of file but got {c:?}"));
498 }
499 }
500 self.skip_ws();
501 }
502
503 self.builder.finish_node();
504 Parse {
506 green_node: self.builder.finish(),
507 errors: self.errors,
508 }
509 }
510 fn bump(&mut self) {
512 let (kind, text) = self.tokens.pop().unwrap();
513 self.builder.token(kind.into(), text.as_str());
514 }
515 fn current(&self) -> Option<SyntaxKind> {
517 self.tokens.last().map(|(kind, _)| *kind)
518 }
519 fn skip_ws(&mut self) {
520 while self.current() == Some(WHITESPACE) || self.current() == Some(NEWLINE) {
521 self.bump()
522 }
523 }
524
525 fn peek_past_ws(&self) -> Option<SyntaxKind> {
526 let mut i = self.tokens.len();
527 while i > 0 {
528 i -= 1;
529 match self.tokens[i].0 {
530 WHITESPACE | NEWLINE => {}
531 _ => return Some(self.tokens[i].0),
532 }
533 }
534 None
535 }
536 }
537
538 let mut tokens = crate::relations::lex(text);
539 tokens.reverse();
540 Parser {
541 tokens,
542 builder: GreenNodeBuilder::new(),
543 errors: Vec::new(),
544 }
545 .parse()
546 }
547
548 type SyntaxNode = rowan::SyntaxNode<Lang>;
554 #[allow(unused)]
555 type SyntaxToken = rowan::SyntaxToken<Lang>;
556 #[allow(unused)]
557 type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;
558
559 impl Parse {
560 fn root_mut(&self) -> Relations {
561 Relations::cast(SyntaxNode::new_root_mut(self.green_node.clone())).unwrap()
562 }
563 }
564
565 macro_rules! ast_node {
566 ($ast:ident, $kind:ident) => {
567 #[repr(transparent)]
569 pub struct $ast(SyntaxNode);
570 impl $ast {
571 #[allow(unused)]
572 fn cast(node: SyntaxNode) -> Option<Self> {
573 if node.kind() == $kind {
574 Some(Self(node))
575 } else {
576 None
577 }
578 }
579 }
580
581 impl std::fmt::Display for $ast {
582 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
583 f.write_str(&self.0.text().to_string())
584 }
585 }
586 };
587 }
588
589 ast_node!(Relations, ROOT);
590 ast_node!(Relation, RELATION);
591
592 impl PartialEq for Relations {
593 fn eq(&self, other: &Self) -> bool {
594 self.relations().collect::<Vec<_>>() == other.relations().collect::<Vec<_>>()
595 }
596 }
597
598 impl PartialEq for Relation {
599 fn eq(&self, other: &Self) -> bool {
600 self.name() == other.name() && self.version() == other.version()
601 }
602 }
603
604 #[cfg(feature = "serde")]
605 impl serde::Serialize for Relations {
606 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
607 let rep = self.to_string();
608 serializer.serialize_str(&rep)
609 }
610 }
611
612 #[cfg(feature = "serde")]
613 impl<'de> serde::Deserialize<'de> for Relations {
614 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
615 let s = String::deserialize(deserializer)?;
616 let relations = s.parse().map_err(serde::de::Error::custom)?;
617 Ok(relations)
618 }
619 }
620
621 impl std::fmt::Debug for Relations {
622 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
623 let mut s = f.debug_struct("Relations");
624
625 for relation in self.relations() {
626 s.field("relation", &relation);
627 }
628
629 s.finish()
630 }
631 }
632
633 impl std::fmt::Debug for Relation {
634 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
635 let mut s = f.debug_struct("Relation");
636
637 s.field("name", &self.name());
638
639 if let Some((vc, version)) = self.version() {
640 s.field("version", &vc);
641 s.field("version", &version);
642 }
643
644 s.finish()
645 }
646 }
647
648 #[cfg(feature = "serde")]
649 impl serde::Serialize for Relation {
650 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
651 let rep = self.to_string();
652 serializer.serialize_str(&rep)
653 }
654 }
655
656 #[cfg(feature = "serde")]
657 impl<'de> serde::Deserialize<'de> for Relation {
658 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
659 let s = String::deserialize(deserializer)?;
660 let relation = s.parse().map_err(serde::de::Error::custom)?;
661 Ok(relation)
662 }
663 }
664
665 impl Default for Relations {
666 fn default() -> Self {
667 Self::new()
668 }
669 }
670
671 impl Relations {
672 pub fn new() -> Self {
674 Self::from(vec![])
675 }
676
677 #[must_use]
679 pub fn wrap_and_sort(self) -> Self {
680 let mut entries = self
681 .relations()
682 .map(|e| e.wrap_and_sort())
683 .collect::<Vec<_>>();
684 entries.sort();
685 Self::from(entries)
687 }
688
689 pub fn relations(&self) -> impl Iterator<Item = Relation> + '_ {
691 self.0.children().filter_map(Relation::cast)
692 }
693
694 pub fn iter(&self) -> impl Iterator<Item = Relation> + '_ {
696 self.relations()
697 }
698
699 pub fn get_relation(&self, idx: usize) -> Option<Relation> {
701 self.relations().nth(idx)
702 }
703
704 pub fn remove_relation(&mut self, idx: usize) -> Relation {
706 let mut relation = self.get_relation(idx).unwrap();
707 relation.remove();
708 relation
709 }
710
711 pub fn insert(&mut self, idx: usize, relation: Relation) {
713 let is_empty = !self.0.children_with_tokens().any(|n| n.kind() == COMMA);
714 let (position, new_children) = if let Some(current_relation) = self.relations().nth(idx)
715 {
716 let to_insert: Vec<NodeOrToken<GreenNode, GreenToken>> = if idx == 0 && is_empty {
717 vec![relation.0.green().into()]
718 } else {
719 vec![
720 relation.0.green().into(),
721 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
722 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
723 ]
724 };
725
726 (current_relation.0.index(), to_insert)
727 } else {
728 let child_count = self.0.children_with_tokens().count();
729 (
730 child_count,
731 if idx == 0 {
732 vec![relation.0.green().into()]
733 } else {
734 vec![
735 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
736 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
737 relation.0.green().into(),
738 ]
739 },
740 )
741 };
742 self.0 = SyntaxNode::new_root_mut(
744 self.0.replace_with(
745 self.0
746 .green()
747 .splice_children(position..position, new_children),
748 ),
749 );
750 }
751
752 pub fn replace(&mut self, idx: usize, relation: Relation) {
754 let current_relation = self.get_relation(idx).unwrap();
755 self.0.splice_children(
756 current_relation.0.index()..current_relation.0.index() + 1,
757 vec![relation.0.into()],
758 );
759 }
760
761 pub fn push(&mut self, relation: Relation) {
763 let pos = self.relations().count();
764 self.insert(pos, relation);
765 }
766
767 pub fn parse_relaxed(s: &str) -> (Relations, Vec<String>) {
769 let parse = parse(s);
770 (parse.root_mut(), parse.errors)
771 }
772
773 pub fn satisfied_by(
775 &self,
776 package_version: impl crate::relations::VersionLookup + Copy,
777 ) -> bool {
778 self.relations().all(|e| e.satisfied_by(package_version))
779 }
780
781 pub fn is_empty(&self) -> bool {
783 self.relations().count() == 0
784 }
785
786 pub fn len(&self) -> usize {
788 self.relations().count()
789 }
790 }
791
792 impl From<Vec<Relation>> for Relations {
793 fn from(entries: Vec<Relation>) -> Self {
794 let mut builder = GreenNodeBuilder::new();
795 builder.start_node(ROOT.into());
796 for (i, relation) in entries.into_iter().enumerate() {
797 if i > 0 {
798 builder.token(COMMA.into(), ",");
799 builder.token(WHITESPACE.into(), " ");
800 }
801 inject(&mut builder, relation.0);
802 }
803 builder.finish_node();
804 Relations(SyntaxNode::new_root_mut(builder.finish()))
805 }
806 }
807
808 impl From<Relation> for Relations {
809 fn from(relation: Relation) -> Self {
810 Self::from(vec![relation])
811 }
812 }
813
814 impl From<Relation> for crate::lossy::Relation {
815 fn from(relation: Relation) -> Self {
816 let mut rel = crate::lossy::Relation::new();
817 rel.name = relation.name();
818 rel.version = relation.version();
819 rel
820 }
821 }
822
823 impl From<Relations> for crate::lossy::Relations {
824 fn from(relations: Relations) -> Self {
825 let mut rels = crate::lossy::Relations::new();
826 for relation in relations.relations() {
827 rels.0.push(relation.into());
828 }
829 rels
830 }
831 }
832
833 impl From<crate::lossy::Relations> for Relations {
834 fn from(relations: crate::lossy::Relations) -> Self {
835 let mut entries = vec![];
836 for relation in relations.iter() {
837 entries.push(relation.clone().into());
838 }
839 Self::from(entries)
840 }
841 }
842
843 impl From<crate::lossy::Relation> for Relation {
844 fn from(relation: crate::lossy::Relation) -> Self {
845 Relation::new(&relation.name, relation.version)
846 }
847 }
848
849 fn inject(builder: &mut GreenNodeBuilder, node: SyntaxNode) {
850 builder.start_node(node.kind().into());
851 for child in node.children_with_tokens() {
852 match child {
853 rowan::NodeOrToken::Node(child) => {
854 inject(builder, child);
855 }
856 rowan::NodeOrToken::Token(token) => {
857 builder.token(token.kind().into(), token.text());
858 }
859 }
860 }
861 builder.finish_node();
862 }
863
864 impl Relation {
865 pub fn new(name: &str, version_constraint: Option<(VersionConstraint, Version)>) -> Self {
879 let mut builder = GreenNodeBuilder::new();
880 builder.start_node(SyntaxKind::RELATION.into());
881 builder.token(IDENT.into(), name);
882 if let Some((vc, version)) = version_constraint {
883 builder.token(WHITESPACE.into(), " ");
884 builder.start_node(SyntaxKind::VERSION.into());
885 builder.token(L_PARENS.into(), "(");
886 builder.start_node(SyntaxKind::CONSTRAINT.into());
887 for c in vc.to_string().chars() {
888 builder.token(
889 match c {
890 '>' => R_ANGLE.into(),
891 '<' => L_ANGLE.into(),
892 '=' => EQUAL.into(),
893 _ => unreachable!(),
894 },
895 c.to_string().as_str(),
896 );
897 }
898 builder.finish_node();
899
900 builder.token(WHITESPACE.into(), " ");
901
902 builder.token(IDENT.into(), version.to_string().as_str());
903
904 builder.token(R_PARENS.into(), ")");
905
906 builder.finish_node();
907 }
908
909 builder.finish_node();
910 Relation(SyntaxNode::new_root_mut(builder.finish()))
911 }
912
913 #[must_use]
922 pub fn wrap_and_sort(&self) -> Self {
923 let mut builder = GreenNodeBuilder::new();
924 builder.start_node(SyntaxKind::RELATION.into());
925 builder.token(IDENT.into(), self.name().as_str());
926 if let Some((vc, version)) = self.version() {
927 builder.token(WHITESPACE.into(), " ");
928 builder.start_node(SyntaxKind::VERSION.into());
929 builder.token(L_PARENS.into(), "(");
930 builder.start_node(SyntaxKind::CONSTRAINT.into());
931 builder.token(
932 match vc {
933 VersionConstraint::GreaterThanEqual => R_ANGLE.into(),
934 VersionConstraint::LessThanEqual => L_ANGLE.into(),
935 VersionConstraint::Equal => EQUAL.into(),
936 VersionConstraint::GreaterThan => R_ANGLE.into(),
937 VersionConstraint::LessThan => L_ANGLE.into(),
938 },
939 vc.to_string().as_str(),
940 );
941 builder.finish_node();
942 builder.token(WHITESPACE.into(), " ");
943 builder.token(IDENT.into(), version.to_string().as_str());
944 builder.token(R_PARENS.into(), ")");
945 builder.finish_node();
946 }
947 builder.finish_node();
948 Relation(SyntaxNode::new_root_mut(builder.finish()))
949 }
950
951 pub fn simple(name: &str) -> Self {
960 Self::new(name, None)
961 }
962
963 pub fn drop_constraint(&mut self) -> bool {
974 let version_token = self.0.children().find(|n| n.kind() == VERSION);
975 if let Some(version_token) = version_token {
976 while let Some(prev) = version_token.prev_sibling_or_token() {
978 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
979 prev.detach();
980 } else {
981 break;
982 }
983 }
984 version_token.detach();
985 return true;
986 }
987
988 false
989 }
990
991 pub fn name(&self) -> String {
1000 self.0
1001 .children_with_tokens()
1002 .find_map(|it| match it {
1003 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
1004 _ => None,
1005 })
1006 .unwrap()
1007 .text()
1008 .to_string()
1009 }
1010
1011 pub fn version(&self) -> Option<(VersionConstraint, Version)> {
1013 let vc = self.0.children().find(|n| n.kind() == VERSION);
1014 let vc = vc.as_ref()?;
1015 let constraint = vc.children().find(|n| n.kind() == CONSTRAINT);
1016
1017 let version = vc.children_with_tokens().find_map(|it| match it {
1018 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
1019 _ => None,
1020 });
1021
1022 if let (Some(constraint), Some(version)) = (constraint, version) {
1023 let vc: VersionConstraint = constraint.to_string().parse().unwrap();
1024 Some((vc, (version.text().to_string()).parse().unwrap()))
1025 } else {
1026 None
1027 }
1028 }
1029
1030 pub fn set_version(&mut self, version_constraint: Option<(VersionConstraint, Version)>) {
1041 let current_version = self.0.children().find(|n| n.kind() == VERSION);
1042 if let Some((vc, version)) = version_constraint {
1043 let mut builder = GreenNodeBuilder::new();
1044 builder.start_node(VERSION.into());
1045 builder.token(L_PARENS.into(), "(");
1046 builder.start_node(CONSTRAINT.into());
1047 match vc {
1048 VersionConstraint::GreaterThanEqual => {
1049 builder.token(R_ANGLE.into(), ">");
1050 builder.token(EQUAL.into(), "=");
1051 }
1052 VersionConstraint::LessThanEqual => {
1053 builder.token(L_ANGLE.into(), "<");
1054 builder.token(EQUAL.into(), "=");
1055 }
1056 VersionConstraint::Equal => {
1057 builder.token(EQUAL.into(), "=");
1058 }
1059 VersionConstraint::GreaterThan => {
1060 builder.token(R_ANGLE.into(), ">");
1061 }
1062 VersionConstraint::LessThan => {
1063 builder.token(L_ANGLE.into(), "<");
1064 }
1065 }
1066 builder.finish_node(); builder.token(WHITESPACE.into(), " ");
1068 builder.token(IDENT.into(), version.to_string().as_str());
1069 builder.token(R_PARENS.into(), ")");
1070 builder.finish_node(); if let Some(current_version) = current_version {
1073 self.0.splice_children(
1074 current_version.index()..current_version.index() + 1,
1075 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
1076 );
1077 } else {
1078 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
1079 let idx = if let Some(name_node) = name_node {
1080 name_node.index() + 1
1081 } else {
1082 0
1083 };
1084 let new_children = vec![
1085 GreenToken::new(WHITESPACE.into(), " ").into(),
1086 builder.finish().into(),
1087 ];
1088 let new_root = SyntaxNode::new_root_mut(
1089 self.0.green().splice_children(idx..idx, new_children),
1090 );
1091 if let Some(parent) = self.0.parent() {
1092 parent.splice_children(
1093 self.0.index()..self.0.index() + 1,
1094 vec![new_root.into()],
1095 );
1096 self.0 = parent
1097 .children_with_tokens()
1098 .nth(self.0.index())
1099 .unwrap()
1100 .clone()
1101 .into_node()
1102 .unwrap();
1103 } else {
1104 self.0 = new_root;
1105 }
1106 }
1107 } else if let Some(current_version) = current_version {
1108 while let Some(prev) = current_version.prev_sibling_or_token() {
1110 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
1111 prev.detach();
1112 } else {
1113 break;
1114 }
1115 }
1116 current_version.detach();
1117 }
1118 }
1119
1120 pub fn remove(&mut self) {
1132 let is_first = !self
1133 .0
1134 .siblings(Direction::Prev)
1135 .skip(1)
1136 .any(|n| n.kind() == RELATION);
1137 if !is_first {
1138 while let Some(n) = self.0.prev_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 break;
1148 }
1149 }
1150 while let Some(n) = self.0.prev_sibling_or_token() {
1151 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1152 n.detach();
1153 } else {
1154 break;
1155 }
1156 }
1157 } else {
1158 while let Some(n) = self.0.next_sibling_or_token() {
1161 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1162 n.detach();
1163 } else if n.kind() == COMMA {
1164 n.detach();
1165 break;
1166 } else {
1167 panic!("Unexpected node: {n:?}");
1168 }
1169 }
1170
1171 while let Some(n) = self.0.next_sibling_or_token() {
1172 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1173 n.detach();
1174 } else {
1175 break;
1176 }
1177 }
1178 }
1179 self.0.detach();
1180 }
1181
1182 pub fn satisfied_by(
1184 &self,
1185 package_version: impl crate::relations::VersionLookup + Copy,
1186 ) -> bool {
1187 let name = self.name();
1188 let version = self.version();
1189 if let Some(version) = version {
1190 if let Some(package_version) = package_version.lookup_version(&name) {
1191 match version.0 {
1192 VersionConstraint::GreaterThanEqual => {
1193 package_version.into_owned() >= version.1
1194 }
1195 VersionConstraint::LessThanEqual => {
1196 package_version.into_owned() <= version.1
1197 }
1198 VersionConstraint::Equal => package_version.into_owned() == version.1,
1199 VersionConstraint::GreaterThan => package_version.into_owned() > version.1,
1200 VersionConstraint::LessThan => package_version.into_owned() < version.1,
1201 }
1202 } else {
1203 false
1204 }
1205 } else {
1206 true
1207 }
1208 }
1209 }
1210
1211 impl PartialOrd for Relation {
1212 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1213 Some(self.cmp(other))
1214 }
1215 }
1216
1217 impl Eq for Relation {}
1218
1219 impl Ord for Relation {
1220 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1221 let name_cmp = self.name().cmp(&other.name());
1223 if name_cmp != std::cmp::Ordering::Equal {
1224 return name_cmp;
1225 }
1226
1227 let self_version = self.version();
1228 let other_version = other.version();
1229
1230 match (self_version, other_version) {
1231 (Some((self_vc, self_version)), Some((other_vc, other_version))) => {
1232 let vc_cmp = self_vc.cmp(&other_vc);
1233 if vc_cmp != std::cmp::Ordering::Equal {
1234 return vc_cmp;
1235 }
1236
1237 self_version.cmp(&other_version)
1238 }
1239 (Some(_), None) => std::cmp::Ordering::Greater,
1240 (None, Some(_)) => std::cmp::Ordering::Less,
1241 (None, None) => std::cmp::Ordering::Equal,
1242 }
1243 }
1244 }
1245
1246 impl std::str::FromStr for Relations {
1247 type Err = String;
1248
1249 fn from_str(s: &str) -> Result<Self, Self::Err> {
1250 let parse = parse(s);
1251 if parse.errors.is_empty() {
1252 Ok(parse.root_mut())
1253 } else {
1254 Err(parse.errors.join("\n"))
1255 }
1256 }
1257 }
1258
1259 impl std::str::FromStr for Relation {
1260 type Err = String;
1261
1262 fn from_str(s: &str) -> Result<Self, Self::Err> {
1263 let rels = s.parse::<Relations>()?;
1264 let mut relations = rels.relations();
1265
1266 let relation = if let Some(relation) = relations.next() {
1267 relation
1268 } else {
1269 return Err("No relation found".to_string());
1270 };
1271
1272 if relations.next().is_some() {
1273 return Err("Multiple relations found".to_string());
1274 }
1275
1276 Ok(relation)
1277 }
1278 }
1279
1280 #[cfg(test)]
1281 mod tests {
1282 use super::*;
1283
1284 #[test]
1285 fn test_parse() {
1286 let input = "cli";
1287 let parsed: Relations = input.parse().unwrap();
1288 assert_eq!(parsed.to_string(), input);
1289 assert_eq!(parsed.relations().count(), 1);
1290 let relation = parsed.relations().next().unwrap();
1291 assert_eq!(relation.to_string(), "cli");
1292 assert_eq!(relation.version(), None);
1293
1294 let input = "cli (>= 0.20.21)";
1295 let parsed: Relations = input.parse().unwrap();
1296 assert_eq!(parsed.to_string(), input);
1297 assert_eq!(parsed.relations().count(), 1);
1298 let relation = parsed.relations().next().unwrap();
1299 assert_eq!(relation.to_string(), "cli (>= 0.20.21)");
1300 assert_eq!(
1301 relation.version(),
1302 Some((
1303 VersionConstraint::GreaterThanEqual,
1304 "0.20.21".parse().unwrap()
1305 ))
1306 );
1307 }
1308
1309 #[test]
1310 fn test_multiple() {
1311 let input = "cli (>= 0.20.21), cli (<< 0.21)";
1312 let parsed: Relations = input.parse().unwrap();
1313 assert_eq!(parsed.to_string(), input);
1314 assert_eq!(parsed.relations().count(), 2);
1315 let relation = parsed.relations().next().unwrap();
1316 assert_eq!(relation.to_string(), "cli (>= 0.20.21)");
1317 assert_eq!(
1318 relation.version(),
1319 Some((
1320 VersionConstraint::GreaterThanEqual,
1321 "0.20.21".parse().unwrap()
1322 ))
1323 );
1324 let relation = parsed.relations().nth(1).unwrap();
1325 assert_eq!(relation.to_string(), "cli (<< 0.21)");
1326 assert_eq!(
1327 relation.version(),
1328 Some((VersionConstraint::LessThan, "0.21".parse().unwrap()))
1329 );
1330 }
1331
1332 #[test]
1333 fn test_new() {
1334 let r = Relation::new(
1335 "cli",
1336 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
1337 );
1338
1339 assert_eq!(r.to_string(), "cli (>= 2.0)");
1340 }
1341
1342 #[test]
1343 fn test_drop_constraint() {
1344 let mut r = Relation::new(
1345 "cli",
1346 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
1347 );
1348
1349 r.drop_constraint();
1350
1351 assert_eq!(r.to_string(), "cli");
1352 }
1353
1354 #[test]
1355 fn test_simple() {
1356 let r = Relation::simple("cli");
1357
1358 assert_eq!(r.to_string(), "cli");
1359 }
1360
1361 #[test]
1362 fn test_remove_first_relation() {
1363 let mut rels: Relations = r#"cli (>= 0.20.21), cli (<< 0.21)"#.parse().unwrap();
1364 let removed = rels.remove_relation(0);
1365 assert_eq!(removed.to_string(), "cli (>= 0.20.21)");
1366 assert_eq!(rels.to_string(), "cli (<< 0.21)");
1367 }
1368
1369 #[test]
1370 fn test_remove_last_relation() {
1371 let mut rels: Relations = r#"cli (>= 0.20.21), cli (<< 0.21)"#.parse().unwrap();
1372 rels.remove_relation(1);
1373 assert_eq!(rels.to_string(), "cli (>= 0.20.21)");
1374 }
1375
1376 #[test]
1377 fn test_remove_middle() {
1378 let mut rels: Relations =
1379 r#"cli (>= 0.20.21), cli (<< 0.21), cli (<< 0.22)"#.parse().unwrap();
1380 rels.remove_relation(1);
1381 assert_eq!(rels.to_string(), "cli (>= 0.20.21), cli (<< 0.22)");
1382 }
1383
1384 #[test]
1385 fn test_remove_added() {
1386 let mut rels: Relations = r#"cli (>= 0.20.21)"#.parse().unwrap();
1387 let relation = Relation::simple("cli");
1388 rels.push(relation);
1389 rels.remove_relation(1);
1390 assert_eq!(rels.to_string(), "cli (>= 0.20.21)");
1391 }
1392
1393 #[test]
1394 fn test_push() {
1395 let mut rels: Relations = r#"cli (>= 0.20.21)"#.parse().unwrap();
1396 let relation = Relation::simple("cli");
1397 rels.push(relation);
1398 assert_eq!(rels.to_string(), "cli (>= 0.20.21), cli");
1399 }
1400
1401 #[test]
1402 fn test_push_from_empty() {
1403 let mut rels: Relations = "".parse().unwrap();
1404 let relation = Relation::simple("cli");
1405 rels.push(relation);
1406 assert_eq!(rels.to_string(), "cli");
1407 }
1408
1409 #[test]
1410 fn test_insert() {
1411 let mut rels: Relations = r#"cli (>= 0.20.21), cli (<< 0.21)"#.parse().unwrap();
1412 let relation = Relation::simple("cli");
1413 rels.insert(1, relation);
1414 assert_eq!(rels.to_string(), "cli (>= 0.20.21), cli, cli (<< 0.21)");
1415 }
1416
1417 #[test]
1418 fn test_insert_at_start() {
1419 let mut rels: Relations = r#"cli (>= 0.20.21), cli (<< 0.21)"#.parse().unwrap();
1420 let relation = Relation::simple("cli");
1421 rels.insert(0, relation);
1422 assert_eq!(rels.to_string(), "cli, cli (>= 0.20.21), cli (<< 0.21)");
1423 }
1424
1425 #[test]
1426 fn test_insert_after_error() {
1427 let (mut rels, errors) = Relations::parse_relaxed("@foo@, debhelper (>= 1.0)");
1428 assert_eq!(
1429 errors,
1430 vec![
1431 "expected identifier or comma but got ERROR",
1432 "expected comma or end of file but got Some(IDENT)",
1433 "expected identifier or comma but got ERROR"
1434 ]
1435 );
1436 let relation = Relation::simple("bar");
1437 rels.push(relation);
1438 assert_eq!(rels.to_string(), "@foo@, debhelper (>= 1.0), bar");
1439 }
1440
1441 #[test]
1442 fn test_insert_before_error() {
1443 let (mut rels, errors) = Relations::parse_relaxed("debhelper (>= 1.0), @foo@, bla");
1444 assert_eq!(
1445 errors,
1446 vec![
1447 "expected identifier or comma but got ERROR",
1448 "expected comma or end of file but got Some(IDENT)",
1449 "expected identifier or comma but got ERROR"
1450 ]
1451 );
1452 let relation = Relation::simple("bar");
1453 rels.insert(0, relation);
1454 assert_eq!(rels.to_string(), "bar, debhelper (>= 1.0), @foo@, bla");
1455 }
1456
1457 #[test]
1458 fn test_replace() {
1459 let mut rels: Relations = r#"cli (>= 0.20.21), cli (<< 0.21)"#.parse().unwrap();
1460 let relation = Relation::simple("cli");
1461 rels.replace(1, relation);
1462 assert_eq!(rels.to_string(), "cli (>= 0.20.21), cli");
1463 }
1464
1465 #[test]
1466 fn test_parse_relation() {
1467 let parsed: Relation = "cli (>= 0.20.21)".parse().unwrap();
1468 assert_eq!(parsed.to_string(), "cli (>= 0.20.21)");
1469 assert_eq!(
1470 parsed.version(),
1471 Some((
1472 VersionConstraint::GreaterThanEqual,
1473 "0.20.21".parse().unwrap()
1474 ))
1475 );
1476 assert_eq!(
1477 "foo, bar".parse::<Relation>().unwrap_err(),
1478 "Multiple relations found"
1479 );
1480 assert_eq!("".parse::<Relation>().unwrap_err(), "No relation found");
1481 }
1482
1483 #[test]
1484 fn test_relations_satisfied_by() {
1485 let rels: Relations = "cli (>= 0.20.21), cli (<< 0.21)".parse().unwrap();
1486 let satisfied = |name: &str| -> Option<Version> {
1487 match name {
1488 "cli" => Some("0.20.21".parse().unwrap()),
1489 _ => None,
1490 }
1491 };
1492 assert!(rels.satisfied_by(satisfied));
1493
1494 let satisfied = |name: &str| match name {
1495 "cli" => Some("0.21".parse().unwrap()),
1496 _ => None,
1497 };
1498 assert!(!rels.satisfied_by(satisfied));
1499
1500 let satisfied = |name: &str| match name {
1501 "cli" => Some("0.20.20".parse().unwrap()),
1502 _ => None,
1503 };
1504 assert!(!rels.satisfied_by(satisfied));
1505 }
1506
1507 #[test]
1508 fn test_wrap_and_sort_relation() {
1509 let relation: Relation = " cli (>= 11.0)".parse().unwrap();
1510
1511 let wrapped = relation.wrap_and_sort();
1512
1513 assert_eq!(wrapped.to_string(), "cli (>= 11.0)");
1514 }
1515
1516 #[test]
1517 fn test_wrap_and_sort_relations() {
1518 let relations: Relations = "cli (>= 0.20.21) , \n\n\n\ncli (<< 0.21)".parse().unwrap();
1519
1520 let wrapped = relations.wrap_and_sort();
1521
1522 assert_eq!(wrapped.to_string(), "cli (<< 0.21), cli (>= 0.20.21)");
1523 }
1524
1525 #[cfg(feature = "serde")]
1526 #[test]
1527 fn test_serialize_relations() {
1528 let relations: Relations = "cli (>= 0.20.21), cli (<< 0.21)".parse().unwrap();
1529 let serialized = serde_json::to_string(&relations).unwrap();
1530 assert_eq!(serialized, r#""cli (>= 0.20.21), cli (<< 0.21)""#);
1531 }
1532
1533 #[cfg(feature = "serde")]
1534 #[test]
1535 fn test_deserialize_relations() {
1536 let relations: Relations = "cli (>= 0.20.21), cli (<< 0.21)".parse().unwrap();
1537 let serialized = serde_json::to_string(&relations).unwrap();
1538 let deserialized: Relations = serde_json::from_str(&serialized).unwrap();
1539 assert_eq!(deserialized.to_string(), relations.to_string());
1540 }
1541
1542 #[cfg(feature = "serde")]
1543 #[test]
1544 fn test_serialize_relation() {
1545 let relation: Relation = "cli (>= 0.20.21)".parse().unwrap();
1546 let serialized = serde_json::to_string(&relation).unwrap();
1547 assert_eq!(serialized, r#""cli (>= 0.20.21)""#);
1548 }
1549
1550 #[cfg(feature = "serde")]
1551 #[test]
1552 fn test_deserialize_relation() {
1553 let relation: Relation = "cli (>= 0.20.21)".parse().unwrap();
1554 let serialized = serde_json::to_string(&relation).unwrap();
1555 let deserialized: Relation = serde_json::from_str(&serialized).unwrap();
1556 assert_eq!(deserialized.to_string(), relation.to_string());
1557 }
1558
1559 #[test]
1560 fn test_relation_set_version() {
1561 let mut rel: Relation = "vign".parse().unwrap();
1562 rel.set_version(None);
1563 assert_eq!("vign", rel.to_string());
1564 rel.set_version(Some((
1565 VersionConstraint::GreaterThanEqual,
1566 "2.0".parse().unwrap(),
1567 )));
1568 assert_eq!("vign (>= 2.0)", rel.to_string());
1569 rel.set_version(None);
1570 assert_eq!("vign", rel.to_string());
1571 rel.set_version(Some((
1572 VersionConstraint::GreaterThanEqual,
1573 "2.0".parse().unwrap(),
1574 )));
1575 rel.set_version(Some((
1576 VersionConstraint::GreaterThanEqual,
1577 "1.1".parse().unwrap(),
1578 )));
1579 assert_eq!("vign (>= 1.1)", rel.to_string());
1580 }
1581
1582 #[test]
1583 fn test_wrap_and_sort_removes_empty_entries() {
1584 let relations: Relations = "foo, , bar, ".parse().unwrap();
1585 let wrapped = relations.wrap_and_sort();
1586 assert_eq!(wrapped.to_string(), "bar, foo");
1587 }
1588 }
1589}
1590
1591#[cfg(test)]
1592mod tests {
1593 use super::*;
1594
1595 #[test]
1596 fn test_parse() {
1597 let s = r###"Package: mypackage
1598Title: What the Package Does (One Line, Title Case)
1599Version: 0.0.0.9000
1600Authors@R:
1601 person("First", "Last", , "first.last@example.com", role = c("aut", "cre"),
1602 comment = c(ORCID = "YOUR-ORCID-ID"))
1603Description: What the package does (one paragraph).
1604License: `use_mit_license()`, `use_gpl3_license()` or friends to pick a
1605 license
1606Encoding: UTF-8
1607Roxygen: list(markdown = TRUE)
1608RoxygenNote: 7.3.2
1609"###;
1610 let desc: RDescription = s.parse().unwrap();
1611
1612 assert_eq!(desc.package(), Some("mypackage".to_string()));
1613 assert_eq!(
1614 desc.title(),
1615 Some("What the Package Does (One Line, Title Case)".to_string())
1616 );
1617 assert_eq!(desc.version(), Some("0.0.0.9000".to_string()));
1618 assert_eq!(
1619 desc.authors(),
1620 Some(RCode(
1621 r#"person("First", "Last", , "first.last@example.com", role = c("aut", "cre"),
1622comment = c(ORCID = "YOUR-ORCID-ID"))"#
1623 .to_string()
1624 ))
1625 );
1626 assert_eq!(
1627 desc.description(),
1628 Some("What the package does (one paragraph).".to_string())
1629 );
1630 assert_eq!(
1631 desc.license(),
1632 Some(
1633 "`use_mit_license()`, `use_gpl3_license()` or friends to pick a\nlicense"
1634 .to_string()
1635 )
1636 );
1637 assert_eq!(desc.encoding(), Some("UTF-8".to_string()));
1638 assert_eq!(desc.roxygen(), Some("list(markdown = TRUE)".to_string()));
1639 assert_eq!(desc.roxygen_note(), Some("7.3.2".to_string()));
1640
1641 assert_eq!(desc.to_string(), s);
1642 }
1643
1644 #[test]
1645 fn test_parse_dplyr() {
1646 let s = include_str!("../testdata/dplyr.desc");
1647
1648 let desc: RDescription = s.parse().unwrap();
1649 assert_eq!("dplyr", desc.package().unwrap());
1650 assert_eq!(
1651 "https://dplyr.tidyverse.org, https://github.com/tidyverse/dplyr",
1652 desc.url().unwrap().as_str()
1653 );
1654 }
1655}