@@ -205,9 +205,10 @@ mod locations {
205205 Some ( folded_text. ends_with ( & folded_pattern) )
206206 }
207207
208- /// The most common global program files paths on this system, by process and system architecture .
208+ /// The most common program files paths, as they are available in this environment .
209209 ///
210- /// This omits the 32-bit ARM program files directory, as Git for Windows is never installed there.
210+ /// This omits the global 32-bit ARM program files directory, because Git for Windows is never
211+ /// installed there.
211212 #[ derive( Clone , Debug ) ]
212213 struct ProgramFilesPaths {
213214 /// The program files directory used for whatever architecture this program was built for.
@@ -224,20 +225,24 @@ mod locations {
224225 /// AMD64 programs use the same program files directory while 32-bit x86 and 32-bit ARM
225226 /// programs use two others. Only a 32-bit system has no 64-bit program files directory.
226227 maybe_64bit : Option < PathBuf > ,
228+
229+ /// The per-user `Programs` subdirectory of the user's local application data directory.
230+ user : PathBuf ,
227231 }
228232
229233 impl ProgramFilesPaths {
230- /// Get the three most common kinds of global program files paths without environment variables.
234+ /// Get the four most common kinds of program files paths without environment variables.
231235 ///
232236 /// The idea here is to obtain this information, which the `alternative_locations()` unit
233237 /// test uses to learn the expected alternative locations, without duplicating *any* of the
234238 /// approach used for `ALTERNATIVE_LOCATIONS`, so it can be used to test that. The approach
235239 /// here is also more reliable than using environment variables, but it is a bit more
236240 /// complex, and it requires either additional dependencies or the use of unsafe code.
237241 ///
238- /// This gets `pf_current` and `pf_x86` by the [known folders][known-folders] system. But
239- /// it gets `maybe_pf_64bit` from the registry, as the corresponding known folder is not
240- /// available to 32-bit processes. See the [`KNOWNFOLDDERID`][knownfolderid] documentation.
242+ /// This gets `pf_user`, `pf_current`, and `pf_x86` by the [known folders][known-folders]
243+ /// system. But it gets `maybe_pf_64bit` from the registry, as the corresponding known
244+ /// folder is not available to 32-bit processes. See the [`KNOWNFOLDDERID`][knownfolderid]
245+ /// documentation.
241246 ///
242247 /// If in the future the implementation of `ALTERNATIVE_LOCATIONS` uses these techniques,
243248 /// then this function can be changed to use environment variables and renamed accordingly.
@@ -262,10 +267,15 @@ mod locations {
262267 } )
263268 . ok ( ) ;
264269
270+ // FIXME: This lacks KF_FLAG_DONT_VERIFY, so it errors out if the folder hasn't been created.
271+ let pf_user = get_known_folder_path ( KnownFolder :: UserProgramFiles )
272+ . expect ( "The path where the user's local Programs folder is, or would be created, is known" ) ;
273+
265274 Self {
266275 current : pf_current,
267276 x86 : pf_x86,
268277 maybe_64bit : maybe_pf_64bit,
278+ user : pf_user,
269279 }
270280 }
271281
@@ -274,6 +284,12 @@ mod locations {
274284 /// This checks that `obtain_envlessly()` returned paths that are likely to be correct and
275285 /// that satisfy the most important properties based on the current system and process.
276286 fn validated ( self ) -> Self {
287+ self . validate_global_folders ( ) ;
288+ self . validate_user_folder ( ) ;
289+ self
290+ }
291+
292+ fn validate_global_folders ( & self ) {
277293 match PlatformBitness :: current ( ) . expect ( "Process and system 'bitness' should be available" ) {
278294 PlatformBitness :: Is32on32 => {
279295 assert_eq ! (
@@ -325,66 +341,111 @@ mod locations {
325341 ) ;
326342 }
327343 }
344+ }
328345
329- self
346+ fn validate_user_folder ( & self ) {
347+ let expected = get_known_folder_path ( KnownFolder :: LocalAppData )
348+ . expect ( "The user's local application data directory is available" )
349+ . join ( "Programs" ) ;
350+ assert_eq ! (
351+ self . user, expected,
352+ "The user program files directory is Programs in the local application data directory." ,
353+ ) ;
330354 }
331355 }
332356
333- /// Paths relative to process architecture specific program files directories.
357+ /// Architecture-specific Git for Windows paths relative to particular program files directories.
334358 #[ derive( Clone , Debug ) ]
335359 struct RelativeGitBinPaths < ' a > {
336- x86 : & ' a Path ,
337- maybe_x64 : Option < & ' a Path > ,
338- maybe_arm64 : Option < & ' a Path > ,
360+ global_x86 : & ' a Path ,
361+ maybe_global_x64 : Option < & ' a Path > ,
362+ maybe_global_arm64 : Option < & ' a Path > ,
363+ user_x86 : & ' a Path ,
364+ maybe_user_x64 : Option < & ' a Path > ,
365+ maybe_user_arm64 : Option < & ' a Path > ,
339366 }
340367
341368 impl < ' a > RelativeGitBinPaths < ' a > {
342369 /// Assert that `locations` has the given path prefixes, and extract the suffixes.
343370 fn assert_from ( pf : & ' a ProgramFilesPaths , locations : & ' static [ PathBuf ] ) -> Self {
344371 match locations {
345- [ primary , secondary , tertiary ] => {
372+ [ path1 , path2 , path3 , path4 , path5 , path6 ] => {
346373 let prefix_64bit = pf
347374 . maybe_64bit
348375 . as_ref ( )
349- . expect ( "It gives three paths only if some can be 64-bit" ) ;
350- let suffix_arm64 = primary
376+ . expect ( "It gives six paths only if some can be 64-bit" ) ;
377+ let suffix_user_arm64 = path1
378+ . strip_prefix ( pf. user . as_path ( ) )
379+ . expect ( "It gives the per-user 64-bit ARM64 path and lists it first" ) ;
380+ let suffix_user_x64 = path2
381+ . strip_prefix ( pf. user . as_path ( ) )
382+ . expect ( "It gives the per-user 64-bit x86 path and lists it second" ) ;
383+ let suffix_user_x86 = path3
384+ . strip_prefix ( pf. user . as_path ( ) )
385+ . expect ( "It gives the per-user 32-bit x86 path and lists it third" ) ;
386+ let suffix_global_arm64 = path4
351387 . strip_prefix ( prefix_64bit)
352- . expect ( "It gives the 64-bit ARM64 path and lists it first " ) ;
353- let suffix_x64 = secondary
388+ . expect ( "It gives the global 64-bit ARM64 path and lists it fourth " ) ;
389+ let suffix_global_x64 = path5
354390 . strip_prefix ( prefix_64bit)
355- . expect ( "It gives the 64-bit x86 path and lists it second " ) ;
356- let suffix_x86 = tertiary
391+ . expect ( "It gives the global 64-bit x86 path and lists it fifth " ) ;
392+ let suffix_global_x86 = path6
357393 . strip_prefix ( pf. x86 . as_path ( ) )
358- . expect ( "It gives the 32-bit path and lists it third " ) ;
394+ . expect ( "It gives the global 32-bit path and lists it sixth " ) ;
359395 Self {
360- x86 : suffix_x86,
361- maybe_x64 : Some ( suffix_x64) ,
362- maybe_arm64 : Some ( suffix_arm64) ,
396+ global_x86 : suffix_global_x86,
397+ maybe_global_x64 : Some ( suffix_global_x64) ,
398+ maybe_global_arm64 : Some ( suffix_global_arm64) ,
399+ user_x86 : suffix_user_x86,
400+ maybe_user_x64 : Some ( suffix_user_x64) ,
401+ maybe_user_arm64 : Some ( suffix_user_arm64) ,
363402 }
364403 }
365- [ only] => {
366- assert_eq ! ( pf. maybe_64bit, None , "It gives one path only if none can be 64-bit." ) ;
367- let suffix_x86 = only
404+ [ path1, path2] => {
405+ assert_eq ! ( pf. maybe_64bit, None , "It gives two paths only if none can be 64-bit." ) ;
406+ let suffix_user_x86 = path1
407+ . strip_prefix ( pf. user . as_path ( ) )
408+ . expect ( "It gives the per-user 32-bit path and lists it first" ) ;
409+ let suffix_global_x86 = path2
368410 . strip_prefix ( pf. x86 . as_path ( ) )
369- . expect ( "The one path it gives is the 32-bit path" ) ;
411+ . expect ( "It gives the global 32-bit path and lists it second " ) ;
370412 Self {
371- x86 : suffix_x86,
372- maybe_x64 : None ,
373- maybe_arm64 : None ,
413+ global_x86 : suffix_global_x86,
414+ maybe_global_x64 : None ,
415+ maybe_global_arm64 : None ,
416+ user_x86 : suffix_user_x86,
417+ maybe_user_x64 : None ,
418+ maybe_user_arm64 : None ,
374419 }
375420 }
376- other => panic ! ( "{:?} has length {}, expected 1 or 3 ." , other, other. len( ) ) ,
421+ other => panic ! ( "{:?} has length {}, expected 2 or 6 ." , other, other. len( ) ) ,
377422 }
378423 }
379424
380- /// Assert that the suffixes (relative subdirectories) are the common per-architecture Git install locations.
425+ /// Assert that the suffixes (relative subdirectories) are the common Git install locations.
381426 fn assert_architectures ( & self ) {
382- assert_eq ! ( self . x86, Path :: new( "Git/mingw32/bin" ) ) ;
427+ self . assert_architectures_global ( ) ;
428+ self . assert_architectures_user ( ) ;
429+ }
430+
431+ fn assert_architectures_global ( & self ) {
432+ assert_eq ! ( self . global_x86, Path :: new( "Git/mingw32/bin" ) ) ;
433+
434+ if let Some ( suffix_x64) = self . maybe_global_x64 {
435+ assert_eq ! ( suffix_x64, Path :: new( "Git/mingw64/bin" ) ) ;
436+ }
437+ if let Some ( suffix_arm64) = self . maybe_global_arm64 {
438+ assert_eq ! ( suffix_arm64, Path :: new( "Git/clangarm64/bin" ) ) ;
439+ }
440+ }
441+
442+ fn assert_architectures_user ( & self ) {
443+ assert_eq ! ( self . user_x86, Path :: new( "Git/mingw32/bin" ) ) ;
383444
384- if let Some ( suffix_x64) = self . maybe_x64 {
445+ if let Some ( suffix_x64) = self . maybe_user_x64 {
385446 assert_eq ! ( suffix_x64, Path :: new( "Git/mingw64/bin" ) ) ;
386447 }
387- if let Some ( suffix_arm64) = self . maybe_arm64 {
448+ if let Some ( suffix_arm64) = self . maybe_user_arm64 {
388449 assert_eq ! ( suffix_arm64, Path :: new( "Git/clangarm64/bin" ) ) ;
389450 }
390451 }
0 commit comments