1
1
//! A module for configuration information
2
2
3
+ use std:: cmp:: Ordering ;
3
4
use std:: collections:: { BTreeMap , BTreeSet } ;
4
5
use std:: convert:: AsRef ;
6
+ use std:: fmt:: Formatter ;
5
7
use std:: iter:: Sum ;
6
8
use std:: ops:: Add ;
7
9
use std:: path:: Path ;
10
+ use std:: str:: FromStr ;
8
11
use std:: { fmt, fs} ;
9
12
10
- use anyhow:: Result ;
13
+ use anyhow:: { Context , Result } ;
11
14
use cargo_lock:: package:: GitReference ;
12
15
use cargo_metadata:: Package ;
13
16
use semver:: VersionReq ;
14
17
use serde:: de:: value:: SeqAccessDeserializer ;
15
- use serde:: de:: { Deserializer , SeqAccess , Visitor } ;
18
+ use serde:: de:: { Deserializer , SeqAccess , Unexpected , Visitor } ;
16
19
use serde:: { Deserialize , Serialize , Serializer } ;
17
20
18
21
use crate :: select:: { Select , Selectable } ;
@@ -516,55 +519,21 @@ pub struct CrateId {
516
519
pub name : String ,
517
520
518
521
/// The crate's semantic version
519
- pub version : String ,
522
+ pub version : semver :: Version ,
520
523
}
521
524
522
525
impl CrateId {
523
526
/// Construct a new [CrateId]
524
- pub fn new ( name : String , version : String ) -> Self {
527
+ pub fn new ( name : String , version : semver :: Version ) -> Self {
525
528
Self { name, version }
526
529
}
527
-
528
- /// Compares a [CrateId] against a [cargo_metadata::Package].
529
- pub fn matches ( & self , package : & Package ) -> bool {
530
- // If the package name does not match, it's obviously
531
- // not the right package
532
- if self . name != "*" && self . name != package. name {
533
- return false ;
534
- }
535
-
536
- // First see if the package version matches exactly
537
- if package. version . to_string ( ) == self . version {
538
- return true ;
539
- }
540
-
541
- // If the version provided is the wildcard "*", it matches. Do not
542
- // delegate to the semver crate in this case because semver does not
543
- // consider "*" to match prerelease packages. That's expected behavior
544
- // in the context of declaring package dependencies, but not in the
545
- // context of declaring which versions of preselected packages an
546
- // annotation applies to.
547
- if self . version == "*" {
548
- return true ;
549
- }
550
-
551
- // Next, check to see if the version provided is a semver req and
552
- // check if the package matches the condition
553
- if let Ok ( semver) = VersionReq :: parse ( & self . version ) {
554
- if semver. matches ( & package. version ) {
555
- return true ;
556
- }
557
- }
558
-
559
- false
560
- }
561
530
}
562
531
563
532
impl From < & Package > for CrateId {
564
533
fn from ( package : & Package ) -> Self {
565
534
Self {
566
535
name : package. name . clone ( ) ,
567
- version : package. version . to_string ( ) ,
536
+ version : package. version . clone ( ) ,
568
537
}
569
538
}
570
539
}
@@ -590,16 +559,20 @@ impl<'de> Visitor<'de> for CrateIdVisitor {
590
559
where
591
560
E : serde:: de:: Error ,
592
561
{
593
- v. rsplit_once ( ' ' )
594
- . map ( |( name, version) | CrateId {
595
- name : name. to_string ( ) ,
596
- version : version. to_string ( ) ,
597
- } )
598
- . ok_or_else ( || {
599
- E :: custom ( format ! (
600
- "Expected string value of `{{name}} {{version}}`. Got '{v}'"
601
- ) )
602
- } )
562
+ let ( name, version_str) = v. rsplit_once ( ' ' ) . ok_or_else ( || {
563
+ E :: custom ( format ! (
564
+ "Expected string value of `{{name}} {{version}}`. Got '{v}'"
565
+ ) )
566
+ } ) ?;
567
+ let version = semver:: Version :: parse ( version_str) . map_err ( |err| {
568
+ E :: custom ( format ! (
569
+ "Couldn't parse {version_str} as a semver::Version: {err}"
570
+ ) )
571
+ } ) ?;
572
+ Ok ( CrateId {
573
+ name : name. to_string ( ) ,
574
+ version,
575
+ } )
603
576
}
604
577
}
605
578
@@ -688,7 +661,7 @@ pub struct Config {
688
661
689
662
/// Additional settings to apply to generated crates
690
663
#[ serde( default , skip_serializing_if = "BTreeMap::is_empty" ) ]
691
- pub annotations : BTreeMap < CrateId , CrateAnnotations > ,
664
+ pub annotations : BTreeMap < CrateNameAndVersionReq , CrateAnnotations > ,
692
665
693
666
/// Settings used to determine various render info
694
667
pub rendering : RenderConfig ,
@@ -708,6 +681,178 @@ impl Config {
708
681
}
709
682
}
710
683
684
+ #[ derive( Clone , Debug , PartialEq , Eq , PartialOrd , Ord ) ]
685
+ pub struct CrateNameAndVersionReq {
686
+ /// The name of the crate
687
+ pub name : String ,
688
+
689
+ version_req_string : VersionReqString ,
690
+ }
691
+
692
+ impl Serialize for CrateNameAndVersionReq {
693
+ fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
694
+ where
695
+ S : Serializer ,
696
+ {
697
+ serializer. serialize_str ( & format ! (
698
+ "{} {}" ,
699
+ self . name, self . version_req_string. original
700
+ ) )
701
+ }
702
+ }
703
+
704
+ struct CrateNameAndVersionReqVisitor ;
705
+ impl < ' de > Visitor < ' de > for CrateNameAndVersionReqVisitor {
706
+ type Value = CrateNameAndVersionReq ;
707
+
708
+ fn expecting ( & self , formatter : & mut fmt:: Formatter ) -> fmt:: Result {
709
+ formatter. write_str ( "Expected string value of `{name} {version}`." )
710
+ }
711
+
712
+ fn visit_str < E > ( self , v : & str ) -> Result < Self :: Value , E >
713
+ where
714
+ E : serde:: de:: Error ,
715
+ {
716
+ let ( name, version) = v. rsplit_once ( ' ' ) . ok_or_else ( || {
717
+ E :: custom ( format ! (
718
+ "Expected string value of `{{name}} {{version}}`. Got '{v}'"
719
+ ) )
720
+ } ) ?;
721
+ version
722
+ . parse ( )
723
+ . map ( |version| CrateNameAndVersionReq {
724
+ name : name. to_string ( ) ,
725
+ version_req_string : version,
726
+ } )
727
+ . map_err ( |err| E :: custom ( err. to_string ( ) ) )
728
+ }
729
+ }
730
+
731
+ impl < ' de > Deserialize < ' de > for CrateNameAndVersionReq {
732
+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
733
+ where
734
+ D : serde:: Deserializer < ' de > ,
735
+ {
736
+ deserializer. deserialize_str ( CrateNameAndVersionReqVisitor )
737
+ }
738
+ }
739
+
740
+ /// A version requirement (i.e. a semver::VersionReq) which preserves the original string it was parsed from.
741
+ /// This means that you can report back to the user whether they wrote `1` or `1.0.0` or `^1.0.0` or `>=1,<2`,
742
+ /// and support exact round-trip serialization and deserialization.
743
+ #[ derive( Clone , Debug ) ]
744
+ pub struct VersionReqString {
745
+ original : String ,
746
+
747
+ parsed : VersionReq ,
748
+ }
749
+
750
+ impl FromStr for VersionReqString {
751
+ type Err = anyhow:: Error ;
752
+
753
+ fn from_str ( original : & str ) -> Result < Self , Self :: Err > {
754
+ let parsed = VersionReq :: parse ( original)
755
+ . context ( "VersionReqString must be a valid semver requirement" ) ?;
756
+ Ok ( VersionReqString {
757
+ original : original. to_owned ( ) ,
758
+ parsed,
759
+ } )
760
+ }
761
+ }
762
+
763
+ impl PartialEq for VersionReqString {
764
+ fn eq ( & self , other : & Self ) -> bool {
765
+ self . original == other. original
766
+ }
767
+ }
768
+
769
+ impl Eq for VersionReqString { }
770
+
771
+ impl PartialOrd for VersionReqString {
772
+ fn partial_cmp ( & self , other : & Self ) -> Option < Ordering > {
773
+ Some ( self . cmp ( other) )
774
+ }
775
+ }
776
+
777
+ impl Ord for VersionReqString {
778
+ fn cmp ( & self , other : & Self ) -> Ordering {
779
+ Ord :: cmp ( & self . original , & other. original )
780
+ }
781
+ }
782
+
783
+ impl Serialize for VersionReqString {
784
+ fn serialize < S > ( & self , serializer : S ) -> std:: result:: Result < S :: Ok , S :: Error >
785
+ where
786
+ S : Serializer ,
787
+ {
788
+ serializer. serialize_str ( & self . original )
789
+ }
790
+ }
791
+
792
+ impl < ' de > Deserialize < ' de > for VersionReqString {
793
+ fn deserialize < D > ( deserializer : D ) -> std:: result:: Result < Self , D :: Error >
794
+ where
795
+ D : Deserializer < ' de > ,
796
+ {
797
+ struct StringVisitor ;
798
+
799
+ impl < ' de > Visitor < ' de > for StringVisitor {
800
+ type Value = String ;
801
+
802
+ fn expecting ( & self , formatter : & mut Formatter ) -> fmt:: Result {
803
+ formatter. write_str ( "string of a semver requirement" )
804
+ }
805
+ }
806
+
807
+ let original = deserializer. deserialize_str ( StringVisitor ) ?;
808
+ let parsed = VersionReq :: parse ( & original) . map_err ( |_| {
809
+ serde:: de:: Error :: invalid_value (
810
+ Unexpected :: Str ( & original) ,
811
+ & "a valid semver requirement" ,
812
+ )
813
+ } ) ?;
814
+ Ok ( VersionReqString { original, parsed } )
815
+ }
816
+ }
817
+
818
+ impl CrateNameAndVersionReq {
819
+ #[ cfg( test) ]
820
+ pub fn new ( name : String , version_req_string : VersionReqString ) -> CrateNameAndVersionReq {
821
+ CrateNameAndVersionReq {
822
+ name,
823
+ version_req_string,
824
+ }
825
+ }
826
+
827
+ /// Compares a [CrateNameAndVersionReq] against a [cargo_metadata::Package].
828
+ pub fn matches ( & self , package : & Package ) -> bool {
829
+ // If the package name does not match, it's obviously
830
+ // not the right package
831
+ if self . name != "*" && self . name != package. name {
832
+ return false ;
833
+ }
834
+
835
+ // First see if the package version matches exactly
836
+ if package. version . to_string ( ) == self . version_req_string . original {
837
+ return true ;
838
+ }
839
+
840
+ // If the version provided is the wildcard "*", it matches. Do not
841
+ // delegate to the semver crate in this case because semver does not
842
+ // consider "*" to match prerelease packages. That's expected behavior
843
+ // in the context of declaring package dependencies, but not in the
844
+ // context of declaring which versions of preselected packages an
845
+ // annotation applies to.
846
+ if self . version_req_string . original == "*" {
847
+ return true ;
848
+ }
849
+
850
+ // Next, check to see if the version provided is a semver req and
851
+ // check if the package matches the condition
852
+ self . version_req_string . parsed . matches ( & package. version )
853
+ }
854
+ }
855
+
711
856
#[ cfg( test) ]
712
857
mod test {
713
858
use super :: * ;
@@ -717,21 +862,17 @@ mod test {
717
862
#[ test]
718
863
fn test_crate_id_serde ( ) {
719
864
let id: CrateId = serde_json:: from_str ( "\" crate 0.1.0\" " ) . unwrap ( ) ;
720
- assert_eq ! ( id, CrateId :: new( "crate" . to_owned( ) , "0.1.0" . to_owned( ) ) ) ;
865
+ assert_eq ! (
866
+ id,
867
+ CrateId :: new( "crate" . to_owned( ) , semver:: Version :: new( 0 , 1 , 0 ) )
868
+ ) ;
721
869
assert_eq ! ( serde_json:: to_string( & id) . unwrap( ) , "\" crate 0.1.0\" " ) ;
722
870
}
723
871
724
- #[ test]
725
- fn test_crate_id_serde_semver ( ) {
726
- let semver_id: CrateId = serde_json:: from_str ( "\" crate *\" " ) . unwrap ( ) ;
727
- assert_eq ! ( semver_id, CrateId :: new( "crate" . to_owned( ) , "*" . to_owned( ) ) ) ;
728
- assert_eq ! ( serde_json:: to_string( & semver_id) . unwrap( ) , "\" crate *\" " ) ;
729
- }
730
-
731
872
#[ test]
732
873
fn test_crate_id_matches ( ) {
733
874
let mut package = mock_cargo_metadata_package ( ) ;
734
- let id = CrateId :: new ( "mock-pkg" . to_owned ( ) , "0.1.0" . to_owned ( ) ) ;
875
+ let id = CrateNameAndVersionReq :: new ( "mock-pkg" . to_owned ( ) , "0.1.0" . parse ( ) . unwrap ( ) ) ;
735
876
736
877
package. version = cargo_metadata:: semver:: Version :: new ( 0 , 1 , 0 ) ;
737
878
assert ! ( id. matches( & package) ) ;
@@ -741,19 +882,43 @@ mod test {
741
882
}
742
883
743
884
#[ test]
744
- fn test_crate_id_semver_matches ( ) {
885
+ fn test_crate_name_and_version_req_serde ( ) {
886
+ let id: CrateNameAndVersionReq = serde_json:: from_str ( "\" crate 0.1.0\" " ) . unwrap ( ) ;
887
+ assert_eq ! (
888
+ id,
889
+ CrateNameAndVersionReq :: new(
890
+ "crate" . to_owned( ) ,
891
+ VersionReqString :: from_str( "0.1.0" ) . unwrap( )
892
+ )
893
+ ) ;
894
+ assert_eq ! ( serde_json:: to_string( & id) . unwrap( ) , "\" crate 0.1.0\" " ) ;
895
+ }
896
+
897
+ #[ test]
898
+ fn test_crate_name_and_version_req_serde_semver ( ) {
899
+ let id: CrateNameAndVersionReq = serde_json:: from_str ( "\" crate *\" " ) . unwrap ( ) ;
900
+ assert_eq ! (
901
+ id,
902
+ CrateNameAndVersionReq :: new(
903
+ "crate" . to_owned( ) ,
904
+ VersionReqString :: from_str( "*" ) . unwrap( )
905
+ )
906
+ ) ;
907
+ assert_eq ! ( serde_json:: to_string( & id) . unwrap( ) , "\" crate *\" " ) ;
908
+ }
909
+
910
+ #[ test]
911
+ fn test_crate_name_and_version_req_semver_matches ( ) {
745
912
let mut package = mock_cargo_metadata_package ( ) ;
746
913
package. version = cargo_metadata:: semver:: Version :: new ( 1 , 0 , 0 ) ;
747
- let mut id = CrateId :: new ( "mock-pkg" . to_owned ( ) , "0.1.0" . to_owned ( ) ) ;
748
-
749
- id. version = "*" . to_owned ( ) ;
914
+ let id = CrateNameAndVersionReq :: new ( "mock-pkg" . to_owned ( ) , "*" . parse ( ) . unwrap ( ) ) ;
750
915
assert ! ( id. matches( & package) ) ;
751
916
752
917
let mut prerelease = mock_cargo_metadata_package ( ) ;
753
918
prerelease. version = cargo_metadata:: semver:: Version :: parse ( "1.0.0-pre.0" ) . unwrap ( ) ;
754
919
assert ! ( id. matches( & prerelease) ) ;
755
920
756
- id . version = " <1". to_owned ( ) ;
921
+ let id = CrateNameAndVersionReq :: new ( "mock-pkg" . to_owned ( ) , " <1". parse ( ) . unwrap ( ) ) ;
757
922
assert ! ( !id. matches( & package) ) ;
758
923
}
759
924
@@ -770,7 +935,10 @@ mod test {
770
935
// Annotations
771
936
let annotation = config
772
937
. annotations
773
- . get ( & CrateId :: new ( "rand" . to_owned ( ) , "0.8.5" . to_owned ( ) ) )
938
+ . get ( & CrateNameAndVersionReq :: new (
939
+ "rand" . to_owned ( ) ,
940
+ "0.8.5" . parse ( ) . unwrap ( ) ,
941
+ ) )
774
942
. unwrap ( ) ;
775
943
assert_eq ! (
776
944
annotation. crate_features,
0 commit comments