1- use std:: cmp :: Ordering ;
1+ use std:: collections :: HashSet ;
22use std:: fmt:: { self , Formatter } ;
33use std:: hash;
44use std:: hash:: Hash ;
55use std:: path:: Path ;
6- use std:: sync:: Arc ;
6+ use std:: ptr;
7+ use std:: sync:: Mutex ;
78
89use semver;
910use serde:: de;
@@ -13,19 +14,41 @@ use core::interning::InternedString;
1314use core:: source:: SourceId ;
1415use util:: { CargoResult , ToSemver } ;
1516
17+ lazy_static ! {
18+ static ref PACKAGE_ID_CACHE : Mutex <HashSet <& ' static PackageIdInner >> = Mutex :: new( HashSet :: new( ) ) ;
19+ }
20+
1621/// Identifier for a specific version of a package in a specific source.
17- #[ derive( Clone ) ]
22+ #[ derive( Clone , Copy , Eq , PartialOrd , Ord ) ]
1823pub struct PackageId {
19- inner : Arc < PackageIdInner > ,
24+ inner : & ' static PackageIdInner ,
2025}
2126
22- #[ derive( PartialEq , PartialOrd , Eq , Ord ) ]
27+ #[ derive( PartialOrd , Eq , Ord ) ]
2328struct PackageIdInner {
2429 name : InternedString ,
2530 version : semver:: Version ,
2631 source_id : SourceId ,
2732}
2833
34+ // Custom equality that uses full equality of SourceId, rather than its custom equality.
35+ impl PartialEq for PackageIdInner {
36+ fn eq ( & self , other : & Self ) -> bool {
37+ self . name == other. name
38+ && self . version == other. version
39+ && self . source_id . full_eq ( & other. source_id )
40+ }
41+ }
42+
43+ // Custom hash that is coherent with the custom equality above.
44+ impl Hash for PackageIdInner {
45+ fn hash < S : hash:: Hasher > ( & self , into : & mut S ) {
46+ self . name . hash ( into) ;
47+ self . version . hash ( into) ;
48+ self . source_id . full_hash ( into) ;
49+ }
50+ }
51+
2952impl ser:: Serialize for PackageId {
3053 fn serialize < S > ( & self , s : S ) -> Result < S :: Ok , S :: Error >
3154 where
@@ -64,51 +87,56 @@ impl<'de> de::Deserialize<'de> for PackageId {
6487 } ;
6588 let source_id = SourceId :: from_url ( url) . map_err ( de:: Error :: custom) ?;
6689
67- Ok ( PackageId {
68- inner : Arc :: new ( PackageIdInner {
90+ Ok ( PackageId :: wrap (
91+ PackageIdInner {
6992 name : InternedString :: new ( name) ,
7093 version,
7194 source_id,
72- } ) ,
73- } )
74- }
75- }
76-
77- impl Hash for PackageId {
78- fn hash < S : hash:: Hasher > ( & self , state : & mut S ) {
79- self . inner . name . hash ( state) ;
80- self . inner . version . hash ( state) ;
81- self . inner . source_id . hash ( state) ;
95+ }
96+ ) )
8297 }
8398}
8499
85100impl PartialEq for PackageId {
86101 fn eq ( & self , other : & PackageId ) -> bool {
87- ( * self . inner ) . eq ( & * other. inner )
88- }
89- }
90- impl PartialOrd for PackageId {
91- fn partial_cmp ( & self , other : & PackageId ) -> Option < Ordering > {
92- ( * self . inner ) . partial_cmp ( & * other. inner )
102+ if ptr :: eq ( self . inner , other. inner ) {
103+ return true ;
104+ }
105+ self . inner . name == other . inner . name
106+ && self . inner . version == other . inner . version
107+ && self . inner . source_id == other. inner . source_id
93108 }
94109}
95- impl Eq for PackageId { }
96- impl Ord for PackageId {
97- fn cmp ( & self , other : & PackageId ) -> Ordering {
98- ( * self . inner ) . cmp ( & * other. inner )
110+
111+ impl < ' a > Hash for PackageId {
112+ fn hash < S : hash:: Hasher > ( & self , state : & mut S ) {
113+ self . inner . name . hash ( state) ;
114+ self . inner . version . hash ( state) ;
115+ self . inner . source_id . hash ( state) ;
99116 }
100117}
101118
102119impl PackageId {
103120 pub fn new < T : ToSemver > ( name : & str , version : T , sid : SourceId ) -> CargoResult < PackageId > {
104121 let v = version. to_semver ( ) ?;
105- Ok ( PackageId {
106- inner : Arc :: new ( PackageIdInner {
122+
123+ Ok ( PackageId :: wrap (
124+ PackageIdInner {
107125 name : InternedString :: new ( name) ,
108126 version : v,
109127 source_id : sid,
110- } ) ,
111- } )
128+ }
129+ ) )
130+ }
131+
132+ fn wrap ( inner : PackageIdInner ) -> PackageId {
133+ let mut cache = PACKAGE_ID_CACHE . lock ( ) . unwrap ( ) ;
134+ let inner = cache. get ( & inner) . map ( |& x| x) . unwrap_or_else ( || {
135+ let inner = Box :: leak ( Box :: new ( inner) ) ;
136+ cache. insert ( inner) ;
137+ inner
138+ } ) ;
139+ PackageId { inner }
112140 }
113141
114142 pub fn name ( & self ) -> InternedString {
@@ -122,23 +150,23 @@ impl PackageId {
122150 }
123151
124152 pub fn with_precise ( & self , precise : Option < String > ) -> PackageId {
125- PackageId {
126- inner : Arc :: new ( PackageIdInner {
153+ PackageId :: wrap (
154+ PackageIdInner {
127155 name : self . inner . name ,
128156 version : self . inner . version . clone ( ) ,
129157 source_id : self . inner . source_id . with_precise ( precise) ,
130- } ) ,
131- }
158+ }
159+ )
132160 }
133161
134162 pub fn with_source_id ( & self , source : SourceId ) -> PackageId {
135- PackageId {
136- inner : Arc :: new ( PackageIdInner {
163+ PackageId :: wrap (
164+ PackageIdInner {
137165 name : self . inner . name ,
138166 version : self . inner . version . clone ( ) ,
139167 source_id : source,
140- } ) ,
141- }
168+ }
169+ )
142170 }
143171
144172 pub fn stable_hash < ' a > ( & ' a self , workspace : & ' a Path ) -> PackageIdStableHash < ' a > {
@@ -195,4 +223,27 @@ mod tests {
195223 assert ! ( PackageId :: new( "foo" , "bar" , repo) . is_err( ) ) ;
196224 assert ! ( PackageId :: new( "foo" , "" , repo) . is_err( ) ) ;
197225 }
226+
227+ #[ test]
228+ fn debug ( ) {
229+ let loc = CRATES_IO_INDEX . to_url ( ) . unwrap ( ) ;
230+ let pkg_id = PackageId :: new ( "foo" , "1.0.0" , SourceId :: for_registry ( & loc) . unwrap ( ) ) . unwrap ( ) ;
231+ assert_eq ! ( r#"PackageId { name: "foo", version: "1.0.0", source: "registry `https://github.com/rust-lang/crates.io-index`" }"# , format!( "{:?}" , pkg_id) ) ;
232+
233+ let pretty = r#"
234+ PackageId {
235+ name: "foo",
236+ version: "1.0.0",
237+ source: "registry `https://github.com/rust-lang/crates.io-index`"
238+ }
239+ "# . trim ( ) ;
240+ assert_eq ! ( pretty, format!( "{:#?}" , pkg_id) ) ;
241+ }
242+
243+ #[ test]
244+ fn display ( ) {
245+ let loc = CRATES_IO_INDEX . to_url ( ) . unwrap ( ) ;
246+ let pkg_id = PackageId :: new ( "foo" , "1.0.0" , SourceId :: for_registry ( & loc) . unwrap ( ) ) . unwrap ( ) ;
247+ assert_eq ! ( "foo v1.0.0" , pkg_id. to_string( ) ) ;
248+ }
198249}
0 commit comments