11use crate :: dirs:: crate_prefix;
22use serde_derive:: Deserialize ;
33
4+ /// marker / template vars for generating download urls.
5+ const CRATE_MARKER : & str = "{crate}" ;
6+ const VERSION_MARKER : & str = "{version}" ;
7+ const PREFIX_MARKER : & str = "{prefix}" ;
8+ const LOWERPREFIX_MARKER : & str = "{lowerprefix}" ;
9+ const SHA256_CHECKSUM_MARKER : & str = "{sha256-checksum}" ;
10+ const DOWNLOAD_URL_MARKERS : [ & str ; 5 ] = [
11+ CRATE_MARKER ,
12+ VERSION_MARKER ,
13+ PREFIX_MARKER ,
14+ LOWERPREFIX_MARKER ,
15+ SHA256_CHECKSUM_MARKER ,
16+ ] ;
17+
418/// Global configuration of an index, reflecting the [contents of config.json](https://doc.rust-lang.org/cargo/reference/registries.html#index-format).
519#[ derive( Clone , Debug , Deserialize ) ]
620pub struct IndexConfig {
7- /// Pattern for creating download URLs. Use [`IndexConfig::download_url`] instead.
21+ /// Pattern for creating download URLs. Use [`IndexConfig::download_url`] or
22+ /// [`IndexConfig::download_url_with_checksum`] instead.
823 pub dl : String ,
924 /// Base URL for publishing, etc.
1025 pub api : Option < String > ,
@@ -14,13 +29,27 @@ impl IndexConfig {
1429 /// Get the URL from where the specified package can be downloaded.
1530 /// This method assumes the particular version is present in the registry,
1631 /// and does not verify that it is.
32+ ///
33+ /// Returns `None` when the configured URL requires the
34+ /// `{sha256-checksum}` placeholder. Use [`Self::download_url_with_checksum`]
35+ /// when the checksum is available.
1736 #[ must_use]
1837 pub fn download_url ( & self , name : & str , version : & str ) -> Option < String > {
19- if !self . dl . contains ( "{crate}" )
20- && !self . dl . contains ( "{version}" )
21- && !self . dl . contains ( "{prefix}" )
22- && !self . dl . contains ( "{lowerprefix}" )
23- {
38+ self . download_url_inner ( name, version, None )
39+ }
40+
41+ /// Get the URL from where the specified package can be downloaded, including
42+ /// the package checksum when required by the registry's URL template.
43+ ///
44+ /// This method assumes the particular version is present in the registry,
45+ /// and does not verify that it is.
46+ #[ must_use]
47+ pub fn download_url_with_checksum ( & self , name : & str , version : & str , checksum : & [ u8 ; 32 ] ) -> Option < String > {
48+ self . download_url_inner ( name, version, Some ( checksum) )
49+ }
50+
51+ fn download_url_inner ( & self , name : & str , version : & str , checksum : Option < & [ u8 ; 32 ] > ) -> Option < String > {
52+ if !DOWNLOAD_URL_MARKERS . iter ( ) . any ( |marker| self . dl . contains ( marker) ) {
2453 let mut new = String :: with_capacity ( self . dl . len ( ) + name. len ( ) + version. len ( ) + 10 ) ;
2554 new. push_str ( & self . dl ) ;
2655 new. push ( '/' ) ;
@@ -30,15 +59,99 @@ impl IndexConfig {
3059 new. push_str ( "/download" ) ;
3160 Some ( new)
3261 } else {
33- let mut prefix = String :: with_capacity ( 5 ) ;
34- crate_prefix ( & mut prefix, name, '/' ) ?;
62+ let prefix = if self . dl . contains ( PREFIX_MARKER ) || self . dl . contains ( LOWERPREFIX_MARKER ) {
63+ let mut prefix = String :: with_capacity ( 5 ) ;
64+ crate_prefix ( & mut prefix, name, '/' ) ?;
65+ Some ( prefix)
66+ } else {
67+ None
68+ } ;
69+ let lowerprefix = prefix. as_ref ( ) . map ( |prefix| prefix. to_ascii_lowercase ( ) ) ;
70+ let checksum = checksum. map ( hex:: encode) ;
71+ if self . dl . contains ( SHA256_CHECKSUM_MARKER ) && checksum. is_none ( ) {
72+ return None ;
73+ }
74+
3575 Some (
3676 self . dl
37- . replace ( "{crate}" , name)
38- . replace ( "{version}" , version)
39- . replace ( "{prefix}" , & prefix)
40- . replace ( "{lowerprefix}" , & prefix. to_ascii_lowercase ( ) ) ,
77+ . replace ( CRATE_MARKER , name)
78+ . replace ( VERSION_MARKER , version)
79+ . replace ( PREFIX_MARKER , prefix. as_deref ( ) . unwrap_or_default ( ) )
80+ . replace ( LOWERPREFIX_MARKER , lowerprefix. as_deref ( ) . unwrap_or_default ( ) )
81+ . replace ( SHA256_CHECKSUM_MARKER , checksum. as_deref ( ) . unwrap_or_default ( ) ) ,
4182 )
4283 }
4384 }
4485}
86+
87+ #[ cfg( test) ]
88+ mod tests {
89+ use super :: IndexConfig ;
90+ use crate :: Crate ;
91+
92+ fn config ( dl : & str ) -> IndexConfig {
93+ IndexConfig {
94+ dl : dl. to_owned ( ) ,
95+ api : None ,
96+ }
97+ }
98+
99+ #[ test]
100+ fn appends_the_default_download_path ( ) {
101+ assert_eq ! (
102+ config( "https://example.invalid/crates" )
103+ . download_url( "demo" , "1.2.3" )
104+ . as_deref( ) ,
105+ Some ( "https://example.invalid/crates/demo/1.2.3/download" ) ,
106+ ) ;
107+ }
108+
109+ #[ test]
110+ fn substitutes_all_documented_markers ( ) {
111+ let checksum = [ 0xab ; 32 ] ;
112+ let config = config ( "https://example.invalid/{crate}/{version}/{prefix}/{lowerprefix}/{sha256-checksum}" ) ;
113+
114+ assert_eq ! (
115+ config
116+ . download_url_with_checksum( "MyCrate" , "1.2.3" , & checksum)
117+ . as_deref( ) ,
118+ Some (
119+ "https://example.invalid/MyCrate/1.2.3/My/Cr/my/cr/\
120+ abababababababababababababababababababababababababababababababab"
121+ ) ,
122+ ) ;
123+ }
124+
125+ #[ test]
126+ fn checksum_marker_requires_a_checksum ( ) {
127+ assert_eq ! (
128+ config( "https://example.invalid/{sha256-checksum}" ) . download_url( "demo" , "1.2.3" ) ,
129+ None ,
130+ ) ;
131+ }
132+
133+ #[ test]
134+ fn templates_without_prefix_markers_do_not_build_one ( ) {
135+ assert_eq ! (
136+ config( "https://example.invalid/{crate}/{version}" )
137+ . download_url( "" , "1.2.3" )
138+ . as_deref( ) ,
139+ Some ( "https://example.invalid//1.2.3" ) ,
140+ ) ;
141+ }
142+
143+ #[ test]
144+ fn version_download_url_supplies_its_checksum ( ) {
145+ let krate = Crate :: from_slice (
146+ br#"{"name":"demo","vers":"1.2.3","deps":[],"cksum":"abababababababababababababababababababababababababababababababab","features":{}}"# ,
147+ )
148+ . unwrap ( ) ;
149+
150+ assert_eq ! (
151+ krate. versions( ) [ 0 ]
152+ . download_url( & config( "https://example.invalid/{sha256-checksum}" ) )
153+ . as_deref( ) ,
154+ Some ( "https://example.invalid/abababababababababababababababababababababababababababababababab" ) ,
155+ ) ;
156+ }
157+ }
0 commit comments