1
1
use std:: cmp:: Ordering ;
2
+ use std:: path:: PathBuf ;
2
3
3
4
/// The possible kinds of code source.
4
5
#[ derive( Debug , Clone , PartialEq , Eq , Hash ) ]
@@ -15,6 +16,8 @@ pub enum SourceKind {
15
16
LocalRegistry ,
16
17
/// A directory-based registry.
17
18
Directory ,
19
+ /// A source with paths to patch files (unstable).
20
+ Patched ( PatchInfo ) ,
18
21
}
19
22
20
23
impl SourceKind {
@@ -27,6 +30,8 @@ impl SourceKind {
27
30
SourceKind :: SparseRegistry => None ,
28
31
SourceKind :: LocalRegistry => Some ( "local-registry" ) ,
29
32
SourceKind :: Directory => Some ( "directory" ) ,
33
+ // Patched source URL already includes the `patched+` prefix, see `SourceId::new`
34
+ SourceKind :: Patched ( _) => None ,
30
35
}
31
36
}
32
37
}
@@ -107,6 +112,10 @@ impl Ord for SourceKind {
107
112
( SourceKind :: Directory , _) => Ordering :: Less ,
108
113
( _, SourceKind :: Directory ) => Ordering :: Greater ,
109
114
115
+ ( SourceKind :: Patched ( a) , SourceKind :: Patched ( b) ) => a. cmp ( b) ,
116
+ ( SourceKind :: Patched ( _) , _) => Ordering :: Less ,
117
+ ( _, SourceKind :: Patched ( _) ) => Ordering :: Greater ,
118
+
110
119
( SourceKind :: Git ( a) , SourceKind :: Git ( b) ) => a. cmp ( b) ,
111
120
}
112
121
}
@@ -199,3 +208,101 @@ impl<'a> std::fmt::Display for PrettyRef<'a> {
199
208
Ok ( ( ) )
200
209
}
201
210
}
211
+
212
+ /// Information to find the source package and patch files.
213
+ #[ derive( Debug , Clone , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
214
+ pub struct PatchInfo {
215
+ /// Name of the package to be patched.
216
+ name : String ,
217
+ /// Verision of the package to be patched.
218
+ version : String ,
219
+ /// Absolute paths to patch files.
220
+ ///
221
+ /// These are absolute to ensure Cargo can locate them in the patching phase.
222
+ patches : Vec < PathBuf > ,
223
+ }
224
+
225
+ impl PatchInfo {
226
+ pub fn new ( name : String , version : String , patches : Vec < PathBuf > ) -> PatchInfo {
227
+ PatchInfo {
228
+ name,
229
+ version,
230
+ patches,
231
+ }
232
+ }
233
+
234
+ /// Collects patch information from query string.
235
+ ///
236
+ /// * `name` --- Package name
237
+ /// * `version` --- Package exact version
238
+ /// * `patch` --- Paths to patch files. Mutiple occurrences allowed.
239
+ pub fn from_query (
240
+ query_pairs : impl Iterator < Item = ( impl AsRef < str > , impl AsRef < str > ) > ,
241
+ ) -> Result < PatchInfo , PatchInfoError > {
242
+ let mut name = None ;
243
+ let mut version = None ;
244
+ let mut patches = Vec :: new ( ) ;
245
+ for ( k, v) in query_pairs {
246
+ let v = v. as_ref ( ) ;
247
+ match k. as_ref ( ) {
248
+ "name" => name = Some ( v. to_owned ( ) ) ,
249
+ "version" => version = Some ( v. to_owned ( ) ) ,
250
+ "patch" => patches. push ( PathBuf :: from ( v) ) ,
251
+ _ => { }
252
+ }
253
+ }
254
+ let name = name. ok_or_else ( || PatchInfoError ( "name" ) ) ?;
255
+ let version = version. ok_or_else ( || PatchInfoError ( "version" ) ) ?;
256
+ if patches. is_empty ( ) {
257
+ return Err ( PatchInfoError ( "path" ) ) ;
258
+ }
259
+ Ok ( PatchInfo :: new ( name, version, patches) )
260
+ }
261
+
262
+ /// As a URL query string.
263
+ pub fn as_query ( & self ) -> PatchInfoQuery < ' _ > {
264
+ PatchInfoQuery ( self )
265
+ }
266
+
267
+ pub fn name ( & self ) -> & str {
268
+ self . name . as_str ( )
269
+ }
270
+
271
+ pub fn version ( & self ) -> & str {
272
+ self . version . as_str ( )
273
+ }
274
+
275
+ pub fn patches ( & self ) -> & [ PathBuf ] {
276
+ self . patches . as_slice ( )
277
+ }
278
+ }
279
+
280
+ /// A [`PatchInfo`] that can be `Display`ed as URL query string.
281
+ pub struct PatchInfoQuery < ' a > ( & ' a PatchInfo ) ;
282
+
283
+ impl < ' a > std:: fmt:: Display for PatchInfoQuery < ' a > {
284
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
285
+ write ! ( f, "name=" ) ?;
286
+ for value in url:: form_urlencoded:: byte_serialize ( self . 0 . name . as_bytes ( ) ) {
287
+ write ! ( f, "{value}" ) ?;
288
+ }
289
+ write ! ( f, "&version=" ) ?;
290
+ for value in url:: form_urlencoded:: byte_serialize ( self . 0 . version . as_bytes ( ) ) {
291
+ write ! ( f, "{value}" ) ?;
292
+ }
293
+ for path in & self . 0 . patches {
294
+ write ! ( f, "&patch=" ) ?;
295
+ let path = path. to_str ( ) . expect ( "utf8 patch" ) . replace ( "\\ " , "/" ) ;
296
+ for value in url:: form_urlencoded:: byte_serialize ( path. as_bytes ( ) ) {
297
+ write ! ( f, "{value}" ) ?;
298
+ }
299
+ }
300
+
301
+ Ok ( ( ) )
302
+ }
303
+ }
304
+
305
+ /// Error parsing patch info from URL query string.
306
+ #[ derive( Debug , thiserror:: Error ) ]
307
+ #[ error( "missing query string `{0}`" ) ]
308
+ pub struct PatchInfoError ( & ' static str ) ;
0 commit comments