@@ -48,9 +48,162 @@ pub use builder::{
4848} ;
4949pub use errors:: Error ;
5050pub use pkgspec:: PkgSpec ;
51+ use std:: fmt;
5152
5253/// A crate's unique identifier
53- pub type Kid = cargo_metadata:: PackageId ;
54+ #[ derive( Clone ) ]
55+ pub struct Kid {
56+ pub repr : String ,
57+ // The subslices for each component in name -> version -> source order
58+ components : [ ( usize , usize ) ; 3 ] ,
59+ }
60+
61+ impl Kid {
62+ #[ inline]
63+ pub fn name ( & self ) -> & str {
64+ let ( s, e) = self . components [ 0 ] ;
65+ & self . repr [ s..e]
66+ }
67+
68+ #[ inline]
69+ pub fn version ( & self ) -> & str {
70+ let ( s, e) = self . components [ 1 ] ;
71+ & self . repr [ s..e]
72+ }
73+
74+ #[ inline]
75+ pub fn source ( & self ) -> & str {
76+ let ( s, e) = self . components [ 2 ] ;
77+ & self . repr [ s..e]
78+ }
79+
80+ #[ inline]
81+ pub fn rev ( & self ) -> Option < & str > {
82+ let src = self . source ( ) ;
83+ if src. starts_with ( "git+" ) {
84+ // In old style ids the rev with always be available, but new ones
85+ // only have it if the rev field was actually set, which is unfortunate
86+ if let Some ( ( _, rev) ) = src. split_once ( '#' ) {
87+ Some ( rev)
88+ } else {
89+ src. split_once ( "?rev=" ) . map ( |( _, r) | r)
90+ }
91+ } else {
92+ None
93+ }
94+ }
95+ }
96+
97+ #[ allow( clippy:: fallible_impl_from) ]
98+ impl From < cargo_metadata:: PackageId > for Kid {
99+ fn from ( pid : cargo_metadata:: PackageId ) -> Self {
100+ let repr = pid. repr ;
101+
102+ let gen = || {
103+ let components = if repr. contains ( ' ' ) {
104+ let name = ( 0 , repr. find ( ' ' ) ?) ;
105+ let version = ( name. 1 + 1 , repr[ name. 1 + 1 ..] . find ( ' ' ) ? + name. 1 + 1 ) ;
106+ // Note we skip the open and close parentheses as they are superfluous
107+ // as every source has them, as well as not being present in the new
108+ // stabilized format
109+ let source = ( version. 1 + 2 , repr. len ( ) - 1 ) ;
110+
111+ [ name, version, source]
112+ } else {
113+ let vmn = repr. rfind ( '#' ) ?;
114+ let ( name, version) = if let Some ( split) = repr[ vmn..] . find ( '@' ) {
115+ ( ( vmn + 1 , vmn + split) , ( vmn + split + 1 , repr. len ( ) ) )
116+ } else {
117+ let begin = repr. rfind ( '/' ) ? + 1 ;
118+ let end = if repr. starts_with ( "git+" ) {
119+ repr[ begin..] . find ( '?' ) ? + begin
120+ } else {
121+ vmn
122+ } ;
123+
124+ ( ( begin, end) , ( vmn + 1 , repr. len ( ) ) )
125+ } ;
126+
127+ [ name, version, ( 0 , vmn) ]
128+ } ;
129+
130+ Some ( components)
131+ } ;
132+
133+ if let Some ( components) = gen ( ) {
134+ Self { repr, components }
135+ } else {
136+ panic ! ( "unable to parse package id '{repr}'" ) ;
137+ }
138+ }
139+ }
140+
141+ impl fmt:: Debug for Kid {
142+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
143+ let mut ds = f. debug_struct ( "Kid" ) ;
144+
145+ ds. field ( "name" , & self . name ( ) )
146+ . field ( "version" , & self . version ( ) ) ;
147+
148+ let src = self . source ( ) ;
149+ if src != "registry+https://github.com/rust-lang/crates.io-index" {
150+ ds. field ( "source" , & src) ;
151+ }
152+
153+ ds. finish ( )
154+ }
155+ }
156+
157+ impl fmt:: Display for Kid {
158+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
159+ f. write_str ( & self . repr )
160+ }
161+ }
162+
163+ impl Default for Kid {
164+ fn default ( ) -> Self {
165+ Self {
166+ repr : String :: new ( ) ,
167+ components : [ ( 0 , 0 ) , ( 0 , 0 ) , ( 0 , 0 ) ] ,
168+ }
169+ }
170+ }
171+
172+ impl std:: hash:: Hash for Kid {
173+ fn hash < H : std:: hash:: Hasher > ( & self , state : & mut H ) {
174+ state. write ( self . repr . as_bytes ( ) ) ;
175+ }
176+ }
177+
178+ impl Ord for Kid {
179+ fn cmp ( & self , o : & Self ) -> std:: cmp:: Ordering {
180+ let a = & self . repr ;
181+ let b = & o. repr ;
182+
183+ for ( ar, br) in self . components . into_iter ( ) . zip ( o. components . into_iter ( ) ) {
184+ let ord = a[ ar. 0 ..ar. 1 ] . cmp ( & b[ br. 0 ..br. 1 ] ) ;
185+ if ord != std:: cmp:: Ordering :: Equal {
186+ return ord;
187+ }
188+ }
189+
190+ std:: cmp:: Ordering :: Equal
191+ }
192+ }
193+
194+ impl PartialOrd for Kid {
195+ fn partial_cmp ( & self , other : & Self ) -> Option < std:: cmp:: Ordering > {
196+ Some ( self . cmp ( other) )
197+ }
198+ }
199+
200+ impl Eq for Kid { }
201+
202+ impl PartialEq for Kid {
203+ fn eq ( & self , other : & Self ) -> bool {
204+ self . cmp ( other) == std:: cmp:: Ordering :: Equal
205+ }
206+ }
54207
55208/// The set of features that have been enabled on a crate
56209pub type EnabledFeatures = std:: collections:: BTreeSet < String > ;
@@ -84,8 +237,6 @@ impl PartialEq<DK> for DepKind {
84237 }
85238}
86239
87- use std:: fmt;
88-
89240impl fmt:: Display for DepKind {
90241 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
91242 match self {
@@ -509,29 +660,36 @@ where
509660 ///
510661 /// fn print(krates: &Krates, name: &str) {
511662 /// let req = VersionReq::parse("=0.2").unwrap();
512- /// for vs in krates.search_matches(name, req.clone()).map(|(_, krate) | &krate.version) {
513- /// println!("found version {} matching {}!", vs, req );
663+ /// for vs in krates.search_matches(name, req.clone()).map(|km | &km. krate.version) {
664+ /// println!("found version {vs } matching {req }!");
514665 /// }
515666 /// }
516667 /// ```
517668 pub fn search_matches (
518669 & self ,
519670 name : impl Into < String > ,
520671 req : semver:: VersionReq ,
521- ) -> impl Iterator < Item = ( NodeId , & N ) > {
672+ ) -> impl Iterator < Item = KrateMatch < ' _ , N > > {
522673 let raw_nodes = & self . graph . raw_nodes ( ) [ 0 ..self . krates_end ] ;
523674
524675 let name = name. into ( ) ;
525676
526- raw_nodes. iter ( ) . enumerate ( ) . filter_map ( move |( id, node) | {
527- if let Node :: Krate { krate, .. } = & node. weight {
528- if krate. name ( ) == name && req. matches ( krate. version ( ) ) {
529- return Some ( ( NodeId :: new ( id) , krate) ) ;
677+ raw_nodes
678+ . iter ( )
679+ . enumerate ( )
680+ . filter_map ( move |( index, node) | {
681+ if let Node :: Krate { krate, id, .. } = & node. weight {
682+ if krate. name ( ) == name && req. matches ( krate. version ( ) ) {
683+ return Some ( KrateMatch {
684+ node_id : NodeId :: new ( index) ,
685+ krate,
686+ kid : id,
687+ } ) ;
688+ }
530689 }
531- }
532690
533- None
534- } )
691+ None
692+ } )
535693 }
536694
537695 /// Get an iterator over all of the crates in the graph with the given name,
@@ -541,28 +699,44 @@ where
541699 /// use krates::Krates;
542700 ///
543701 /// fn print_all_versions(krates: &Krates, name: &str) {
544- /// for vs in krates.krates_by_name(name).map(|(_, krate) | &krate.version) {
545- /// println!("found version {}", vs );
702+ /// for vs in krates.krates_by_name(name).map(|km | &km. krate.version) {
703+ /// println!("found version {vs}" );
546704 /// }
547705 /// }
548706 /// ```
549- pub fn krates_by_name ( & self , name : impl Into < String > ) -> impl Iterator < Item = ( NodeId , & N ) > {
707+ pub fn krates_by_name (
708+ & self ,
709+ name : impl Into < String > ,
710+ ) -> impl Iterator < Item = KrateMatch < ' _ , N > > {
550711 let raw_nodes = & self . graph . raw_nodes ( ) [ 0 ..self . krates_end ] ;
551712
552713 let name = name. into ( ) ;
553714
554- raw_nodes. iter ( ) . enumerate ( ) . filter_map ( move |( id, node) | {
555- if let Node :: Krate { krate, .. } = & node. weight {
556- if krate. name ( ) == name {
557- return Some ( ( NodeId :: new ( id) , krate) ) ;
715+ raw_nodes
716+ . iter ( )
717+ . enumerate ( )
718+ . filter_map ( move |( index, node) | {
719+ if let Node :: Krate { krate, id, .. } = & node. weight {
720+ if krate. name ( ) == name {
721+ return Some ( KrateMatch {
722+ node_id : NodeId :: new ( index) ,
723+ krate,
724+ kid : id,
725+ } ) ;
726+ }
558727 }
559- }
560728
561- None
562- } )
729+ None
730+ } )
563731 }
564732}
565733
734+ pub struct KrateMatch < ' graph , N > {
735+ pub krate : & ' graph N ,
736+ pub kid : & ' graph Kid ,
737+ pub node_id : NodeId ,
738+ }
739+
566740impl < N , E > std:: ops:: Index < NodeId > for Krates < N , E > {
567741 type Output = N ;
568742
@@ -586,3 +760,31 @@ impl<N, E> std::ops::Index<usize> for Krates<N, E> {
586760 }
587761 }
588762}
763+
764+ #[ cfg( test) ]
765+ mod tests {
766+ #[ test]
767+ fn converts_package_ids ( ) {
768+ let ids = [
769+ ( "registry+https://github.com/rust-lang/crates.io-index#[email protected] " , "ab_glyph" , "0.2.22" , "registry+https://github.com/rust-lang/crates.io-index" , None ) , 770+ ( "git+https://github.com/EmbarkStudios/egui-stylist?rev=3900e8aedc5801e42c1bb747cfd025615bf3b832#0.2.0" , "egui-stylist" , "0.2.0" , "git+https://github.com/EmbarkStudios/egui-stylist?rev=3900e8aedc5801e42c1bb747cfd025615bf3b832" , Some ( "3900e8aedc5801e42c1bb747cfd025615bf3b832" ) ) ,
771+ ( "path+file:///home/jake/code/ark/components/allocator#[email protected] " , "ark-allocator" , "0.1.0" , "path+file:///home/jake/code/ark/components/allocator" , None ) , 772+ ( "git+https://github.com/EmbarkStudios/ash?branch=nv-low-latency2#0.38.0+1.3.269" , "ash" , "0.38.0+1.3.269" , "git+https://github.com/EmbarkStudios/ash?branch=nv-low-latency2" , None ) ,
773+ ( "git+https://github.com/EmbarkStudios/fsr-rs?branch=nv-low-latency2#[email protected] " , "fsr" , "0.1.7" , "git+https://github.com/EmbarkStudios/fsr-rs?branch=nv-low-latency2" , None ) , 774+ ( "fuser 0.4.1 (git+https://github.com/cberner/fuser?branch=master#b2e7622942e52a28ffa85cdaf48e28e982bb6923)" , "fuser" , "0.4.1" , "git+https://github.com/cberner/fuser?branch=master#b2e7622942e52a28ffa85cdaf48e28e982bb6923" , Some ( "b2e7622942e52a28ffa85cdaf48e28e982bb6923" ) ) ,
775+ ( "a 0.1.0 (path+file:///home/jake/code/krates/tests/ws/a)" , "a" , "0.1.0" , "path+file:///home/jake/code/krates/tests/ws/a" , None ) ,
776+ ( "bindgen 0.59.2 (registry+https://github.com/rust-lang/crates.io-index)" , "bindgen" , "0.59.2" , "registry+https://github.com/rust-lang/crates.io-index" , None ) ,
777+ ] ;
778+
779+ for ( repr, name, version, source, rev) in ids {
780+ let kid = super :: Kid :: from ( cargo_metadata:: PackageId {
781+ repr : repr. to_owned ( ) ,
782+ } ) ;
783+
784+ assert_eq ! ( kid. name( ) , name) ;
785+ assert_eq ! ( kid. version( ) , version) ;
786+ assert_eq ! ( kid. source( ) , source) ;
787+ assert_eq ! ( kid. rev( ) , rev) ;
788+ }
789+ }
790+ }
0 commit comments