@@ -5,9 +5,12 @@ use cargo::util::Sha256;
5
5
use flate2:: write:: GzEncoder ;
6
6
use flate2:: Compression ;
7
7
use std:: collections:: HashMap ;
8
+ use std:: fmt:: Write as _;
8
9
use std:: fs:: { self , File } ;
9
- use std:: io:: prelude:: * ;
10
+ use std:: io:: { BufRead , BufReader , Write } ;
11
+ use std:: net:: TcpListener ;
10
12
use std:: path:: { Path , PathBuf } ;
13
+ use std:: thread;
11
14
use tar:: { Builder , Header } ;
12
15
use url:: Url ;
13
16
@@ -70,6 +73,183 @@ pub fn generate_alt_dl_url(name: &str) -> String {
70
73
format ! ( "{}/{{crate}}/{{version}}/{{crate}}-{{version}}.crate" , base)
71
74
}
72
75
76
+ /// A builder for initializing registries.
77
+ pub struct RegistryBuilder {
78
+ /// If `true`, adds source replacement for crates.io to a registry on the filesystem.
79
+ replace_crates_io : bool ,
80
+ /// If `true`, configures a registry named "alternative".
81
+ alternative : bool ,
82
+ /// If set, sets the API url for the "alternative" registry.
83
+ /// This defaults to a directory on the filesystem.
84
+ alt_api_url : Option < String > ,
85
+ /// If `true`, configures `.cargo/credentials` with some tokens.
86
+ add_tokens : bool ,
87
+ }
88
+
89
+ impl RegistryBuilder {
90
+ pub fn new ( ) -> RegistryBuilder {
91
+ RegistryBuilder {
92
+ replace_crates_io : true ,
93
+ alternative : false ,
94
+ alt_api_url : None ,
95
+ add_tokens : true ,
96
+ }
97
+ }
98
+
99
+ /// Sets whether or not to replace crates.io with a registry on the filesystem.
100
+ /// Default is `true`.
101
+ pub fn replace_crates_io ( & mut self , replace : bool ) -> & mut Self {
102
+ self . replace_crates_io = replace;
103
+ self
104
+ }
105
+
106
+ /// Sets whether or not to initialize an alternative registry named "alternative".
107
+ /// Default is `false`.
108
+ pub fn alternative ( & mut self , alt : bool ) -> & mut Self {
109
+ self . alternative = alt;
110
+ self
111
+ }
112
+
113
+ /// Sets the API url for the "alternative" registry.
114
+ /// Defaults to a path on the filesystem ([`alt_api_path`]).
115
+ pub fn alternative_api_url ( & mut self , url : & str ) -> & mut Self {
116
+ self . alternative = true ;
117
+ self . alt_api_url = Some ( url. to_string ( ) ) ;
118
+ self
119
+ }
120
+
121
+ /// Sets whether or not to initialize `.cargo/credentials` with some tokens.
122
+ /// Defaults to `true`.
123
+ pub fn add_tokens ( & mut self , add : bool ) -> & mut Self {
124
+ self . add_tokens = add;
125
+ self
126
+ }
127
+
128
+ /// Initializes the registries.
129
+ pub fn build ( & self ) {
130
+ let config_path = paths:: home ( ) . join ( ".cargo/config" ) ;
131
+ if config_path. exists ( ) {
132
+ panic ! (
133
+ "{} already exists, the registry may only be initialized once, \
134
+ and must be done before the config file is created",
135
+ config_path. display( )
136
+ ) ;
137
+ }
138
+ t ! ( fs:: create_dir_all( config_path. parent( ) . unwrap( ) ) ) ;
139
+ let mut config = String :: new ( ) ;
140
+ if self . replace_crates_io {
141
+ write ! (
142
+ & mut config,
143
+ "
144
+ [source.crates-io]
145
+ replace-with = 'dummy-registry'
146
+
147
+ [source.dummy-registry]
148
+ registry = '{}'
149
+ " ,
150
+ registry_url( )
151
+ )
152
+ . unwrap ( ) ;
153
+ }
154
+ if self . alternative {
155
+ write ! (
156
+ config,
157
+ "
158
+ [registries.alternative]
159
+ index = '{}'
160
+ " ,
161
+ alt_registry_url( )
162
+ )
163
+ . unwrap ( ) ;
164
+ }
165
+ t ! ( fs:: write( & config_path, config) ) ;
166
+
167
+ if self . add_tokens {
168
+ let credentials = paths:: home ( ) . join ( ".cargo/credentials" ) ;
169
+ t ! ( fs:: write(
170
+ & credentials,
171
+ r#"
172
+ [registry]
173
+ token = "api-token"
174
+
175
+ [registries.alternative]
176
+ token = "api-token"
177
+ "#
178
+ ) ) ;
179
+ }
180
+
181
+ if self . replace_crates_io {
182
+ init_registry (
183
+ registry_path ( ) ,
184
+ dl_url ( ) . into_string ( ) ,
185
+ api_url ( ) ,
186
+ api_path ( ) ,
187
+ ) ;
188
+ }
189
+
190
+ if self . alternative {
191
+ init_registry (
192
+ alt_registry_path ( ) ,
193
+ alt_dl_url ( ) ,
194
+ self . alt_api_url
195
+ . as_ref ( )
196
+ . map_or_else ( alt_api_url, |url| Url :: parse ( & url) . expect ( "valid url" ) ) ,
197
+ alt_api_path ( ) ,
198
+ ) ;
199
+ }
200
+ }
201
+
202
+ /// Initializes the registries, and sets up an HTTP server for the
203
+ /// "alternative" registry.
204
+ ///
205
+ /// The given callback takes a `Vec` of headers when a request comes in.
206
+ /// The first entry should be the HTTP command, such as
207
+ /// `PUT /api/v1/crates/new HTTP/1.1`.
208
+ ///
209
+ /// The callback should return the HTTP code for the response, and the
210
+ /// response body.
211
+ ///
212
+ /// This method returns a `JoinHandle` which you should call
213
+ /// `.join().unwrap()` on before exiting the test.
214
+ pub fn build_api_server < ' a > (
215
+ & mut self ,
216
+ handler : & ' static ( dyn ( Fn ( Vec < String > ) -> ( u32 , & ' a dyn AsRef < [ u8 ] > ) ) + Sync ) ,
217
+ ) -> thread:: JoinHandle < ( ) > {
218
+ let server = TcpListener :: bind ( "127.0.0.1:0" ) . unwrap ( ) ;
219
+ let addr = server. local_addr ( ) . unwrap ( ) ;
220
+ let api_url = format ! ( "http://{}" , addr) ;
221
+
222
+ self . replace_crates_io ( false )
223
+ . alternative_api_url ( & api_url)
224
+ . build ( ) ;
225
+
226
+ let t = thread:: spawn ( move || {
227
+ let mut conn = BufReader :: new ( server. accept ( ) . unwrap ( ) . 0 ) ;
228
+ let headers: Vec < _ > = ( & mut conn)
229
+ . lines ( )
230
+ . map ( |s| s. unwrap ( ) )
231
+ . take_while ( |s| s. len ( ) > 2 )
232
+ . map ( |s| s. trim ( ) . to_string ( ) )
233
+ . collect ( ) ;
234
+ let ( code, response) = handler ( headers) ;
235
+ let response = response. as_ref ( ) ;
236
+ let stream = conn. get_mut ( ) ;
237
+ write ! (
238
+ stream,
239
+ "HTTP/1.1 {}\r \n \
240
+ Content-Length: {}\r \n \
241
+ \r \n ",
242
+ code,
243
+ response. len( )
244
+ )
245
+ . unwrap ( ) ;
246
+ stream. write_all ( response) . unwrap ( ) ;
247
+ } ) ;
248
+
249
+ t
250
+ }
251
+ }
252
+
73
253
/// A builder for creating a new package in a registry.
74
254
///
75
255
/// This uses "source replacement" using an automatically generated
@@ -162,70 +342,28 @@ pub struct Dependency {
162
342
optional : bool ,
163
343
}
164
344
345
+ /// Initializes the on-disk registry and sets up the config so that crates.io
346
+ /// is replaced with the one on disk.
165
347
pub fn init ( ) {
166
348
let config = paths:: home ( ) . join ( ".cargo/config" ) ;
167
- t ! ( fs:: create_dir_all( config. parent( ) . unwrap( ) ) ) ;
168
349
if config. exists ( ) {
169
350
return ;
170
351
}
171
- t ! ( fs:: write(
172
- & config,
173
- format!(
174
- r#"
175
- [source.crates-io]
176
- registry = 'https://wut'
177
- replace-with = 'dummy-registry'
178
-
179
- [source.dummy-registry]
180
- registry = '{reg}'
181
-
182
- [registries.alternative]
183
- index = '{alt}'
184
- "# ,
185
- reg = registry_url( ) ,
186
- alt = alt_registry_url( )
187
- )
188
- ) ) ;
189
- let credentials = paths:: home ( ) . join ( ".cargo/credentials" ) ;
190
- t ! ( fs:: write(
191
- & credentials,
192
- r#"
193
- [registry]
194
- token = "api-token"
195
-
196
- [registries.alternative]
197
- token = "api-token"
198
- "#
199
- ) ) ;
352
+ RegistryBuilder :: new ( ) . build ( ) ;
353
+ }
200
354
201
- // Initialize a new registry.
202
- init_registry (
203
- registry_path ( ) ,
204
- dl_url ( ) . into_string ( ) ,
205
- api_url ( ) ,
206
- api_path ( ) ,
207
- ) ;
208
-
209
- // Initialize an alternative registry.
210
- init_registry (
211
- alt_registry_path ( ) ,
212
- alt_dl_url ( ) ,
213
- alt_api_url ( ) ,
214
- alt_api_path ( ) ,
215
- ) ;
355
+ /// Variant of `init` that initializes the "alternative" registry.
356
+ pub fn alt_init ( ) {
357
+ RegistryBuilder :: new ( ) . alternative ( true ) . build ( ) ;
216
358
}
217
359
360
+ /// Creates a new on-disk registry.
218
361
pub fn init_registry ( registry_path : PathBuf , dl_url : String , api_url : Url , api_path : PathBuf ) {
219
362
// Initialize a new registry.
220
363
repo ( & registry_path)
221
364
. file (
222
365
"config.json" ,
223
- & format ! (
224
- r#"
225
- {{"dl":"{}","api":"{}"}}
226
- "# ,
227
- dl_url, api_url
228
- ) ,
366
+ & format ! ( r#"{{"dl":"{}","api":"{}"}}"# , dl_url, api_url) ,
229
367
)
230
368
. build ( ) ;
231
369
fs:: create_dir_all ( api_path. join ( "api/v1/crates" ) ) . unwrap ( ) ;
0 commit comments