@@ -31,91 +31,77 @@ import { ExecFileError, execFile, execFileStreamOutput } from "../utilities/util
31
31
import { Version } from "../utilities/version" ;
32
32
import { SwiftlyConfig } from "./ToolchainVersion" ;
33
33
34
- const ListResult = z . object ( {
35
- toolchains : z . array (
36
- z . object ( {
37
- inUse : z . boolean ( ) ,
38
- isDefault : z . boolean ( ) ,
39
- version : z . union ( [
40
- z . object ( {
41
- major : z . union ( [ z . number ( ) , z . undefined ( ) ] ) ,
42
- minor : z . union ( [ z . number ( ) , z . undefined ( ) ] ) ,
43
- patch : z . union ( [ z . number ( ) , z . undefined ( ) ] ) ,
44
- name : z . string ( ) ,
45
- type : z . literal ( "stable" ) ,
46
- } ) ,
47
- z . object ( {
48
- major : z . union ( [ z . number ( ) , z . undefined ( ) ] ) ,
49
- minor : z . union ( [ z . number ( ) , z . undefined ( ) ] ) ,
50
- branch : z . string ( ) ,
51
- date : z . string ( ) ,
52
- name : z . string ( ) ,
53
- type : z . literal ( "snapshot" ) ,
54
- } ) ,
55
- z . object ( {
56
- name : z . string ( ) ,
57
- type : z . literal ( "system" ) ,
58
- } ) ,
59
- z . object ( ) ,
60
- ] ) ,
61
- } )
62
- ) ,
63
- } ) ;
64
-
65
- const InUseVersionResult = z . object ( {
66
- version : z . string ( ) ,
34
+ const SystemVersion = z . object ( {
35
+ type : z . literal ( "system" ) ,
36
+ name : z . string ( ) ,
67
37
} ) ;
38
+ export type SystemVersion = z . infer < typeof SystemVersion > ;
68
39
69
40
const StableVersion = z . object ( {
41
+ type : z . literal ( "stable" ) ,
42
+ name : z . string ( ) ,
43
+
70
44
major : z . number ( ) ,
71
45
minor : z . number ( ) ,
72
46
patch : z . number ( ) ,
73
- name : z . string ( ) ,
74
- type : z . literal ( "stable" ) ,
75
47
} ) ;
76
-
77
48
export type StableVersion = z . infer < typeof StableVersion > ;
78
49
79
50
const SnapshotVersion = z . object ( {
80
- major : z . union ( [ z . number ( ) , z . undefined ( ) ] ) ,
81
- minor : z . union ( [ z . number ( ) , z . undefined ( ) ] ) ,
51
+ type : z . literal ( "snapshot" ) ,
52
+ name : z . string ( ) ,
53
+
54
+ major : z . optional ( z . number ( ) ) ,
55
+ minor : z . optional ( z . number ( ) ) ,
82
56
branch : z . string ( ) ,
83
57
date : z . string ( ) ,
84
- name : z . string ( ) ,
85
- type : z . literal ( "snapshot" ) ,
86
58
} ) ;
87
-
88
59
export type SnapshotVersion = z . infer < typeof SnapshotVersion > ;
89
60
90
- export interface SwiftlyToolchain {
61
+ export type ToolchainVersion = SystemVersion | StableVersion | SnapshotVersion ;
62
+
63
+ export interface AvailableToolchain {
91
64
inUse : boolean ;
92
65
installed : boolean ;
93
66
isDefault : boolean ;
94
- version : StableVersion | SnapshotVersion ;
67
+ version : ToolchainVersion ;
95
68
}
96
69
97
- const AvailableToolchain = z . object ( {
98
- inUse : z . boolean ( ) ,
99
- installed : z . boolean ( ) ,
100
- isDefault : z . boolean ( ) ,
101
- version : z . union ( [ StableVersion , SnapshotVersion , z . object ( ) ] ) ,
70
+ const SwiftlyListResult = z . object ( {
71
+ toolchains : z . array (
72
+ z . object ( {
73
+ inUse : z . boolean ( ) ,
74
+ isDefault : z . boolean ( ) ,
75
+ version : z . union ( [
76
+ SystemVersion ,
77
+ StableVersion ,
78
+ SnapshotVersion ,
79
+ // Allow matching against unexpected future version types
80
+ z . object ( ) ,
81
+ ] ) ,
82
+ } )
83
+ ) ,
102
84
} ) ;
103
- type AvailableToolchain = z . infer < typeof AvailableToolchain > ;
104
-
105
- export function isStableVersion (
106
- version : StableVersion | SnapshotVersion
107
- ) : version is StableVersion {
108
- return version . type === "stable" ;
109
- }
110
85
111
- export function isSnapshotVersion (
112
- version : StableVersion | SnapshotVersion
113
- ) : version is SnapshotVersion {
114
- return version . type === "snapshot" ;
115
- }
86
+ const SwiftlyListAvailableResult = z . object ( {
87
+ toolchains : z . array (
88
+ z . object ( {
89
+ inUse : z . boolean ( ) ,
90
+ installed : z . boolean ( ) ,
91
+ isDefault : z . boolean ( ) ,
92
+ version : z . union ( [
93
+ SystemVersion ,
94
+ StableVersion ,
95
+ SnapshotVersion ,
96
+ // Allow matching against unexpected future version types
97
+ z . object ( ) ,
98
+ ] ) ,
99
+ } )
100
+ ) ,
101
+ } ) ;
116
102
117
- const ListAvailableResult = z . object ( {
118
- toolchains : z . array ( AvailableToolchain ) ,
103
+ const InUseVersionResult = z . object ( {
104
+ version : z . string ( ) ,
119
105
} ) ;
120
106
121
107
export interface SwiftlyProgressData {
@@ -228,6 +214,8 @@ export class Swiftly {
228
214
/**
229
215
* Finds the list of toolchains installed via Swiftly.
230
216
*
217
+ * Toolchains will be sorted by version number in descending order.
218
+ *
231
219
* @returns an array of toolchain version names.
232
220
*/
233
221
public static async list ( logger ?: SwiftLogger ) : Promise < string [ ] > {
@@ -250,10 +238,13 @@ export class Swiftly {
250
238
private static async listUsingJSONFormat ( logger ?: SwiftLogger ) : Promise < string [ ] > {
251
239
try {
252
240
const { stdout } = await execFile ( "swiftly" , [ "list" , "--format=json" ] ) ;
253
- const response = ListResult . parse ( JSON . parse ( stdout ) ) ;
254
- return response . toolchains
255
- . filter ( t => [ "stable" , "snapshot" , "system" ] . includes ( t . version ?. type ) )
256
- . map ( t => t . version . name ) ;
241
+ return SwiftlyListResult . parse ( JSON . parse ( stdout ) )
242
+ . toolchains . map ( toolchain => toolchain . version )
243
+ . filter ( ( version ) : version is ToolchainVersion =>
244
+ [ "system" , "stable" , "snapshot" ] . includes ( version . type )
245
+ )
246
+ . sort ( compareSwiftlyToolchainVersion )
247
+ . map ( version => version . name ) ;
257
248
} catch ( error ) {
258
249
logger ?. error ( `Failed to retrieve Swiftly installations: ${ error } ` ) ;
259
250
return [ ] ;
@@ -274,8 +265,14 @@ export class Swiftly {
274
265
if ( ! Array . isArray ( installedToolchains ) ) {
275
266
return [ ] ;
276
267
}
277
- return installedToolchains . filter (
278
- ( toolchain ) : toolchain is string => typeof toolchain === "string"
268
+ return (
269
+ installedToolchains
270
+ . filter ( ( toolchain ) : toolchain is string => typeof toolchain === "string" )
271
+ // Sort alphabetically in descending order.
272
+ //
273
+ // This isn't perfect (e.g. "5.10" will come before "5.9"), but this is
274
+ // good enough for legacy support.
275
+ . sort ( ( lhs , rhs ) => rhs . localeCompare ( lhs ) )
279
276
) ;
280
277
} catch ( error ) {
281
278
logger ?. error ( `Failed to retrieve Swiftly installations: ${ error } ` ) ;
@@ -421,14 +418,16 @@ export class Swiftly {
421
418
/**
422
419
* Lists all toolchains available for installation from swiftly.
423
420
*
421
+ * Toolchains will be sorted by version number in descending order.
422
+ *
424
423
* @param branch Optional branch to filter available toolchains (e.g., "main" for snapshots).
425
424
* @param logger Optional logger for error reporting.
426
425
* @returns Array of available toolchains.
427
426
*/
428
427
public static async listAvailable (
429
428
branch ?: string ,
430
429
logger ?: SwiftLogger
431
- ) : Promise < SwiftlyToolchain [ ] > {
430
+ ) : Promise < AvailableToolchain [ ] > {
432
431
if ( ! this . isSupported ( ) ) {
433
432
return [ ] ;
434
433
}
@@ -450,10 +449,11 @@ export class Swiftly {
450
449
args . push ( branch ) ;
451
450
}
452
451
const { stdout : availableStdout } = await execFile ( "swiftly" , args ) ;
453
- const result = ListAvailableResult . parse ( JSON . parse ( availableStdout ) ) ;
454
- return result . toolchains . filter ( ( t ) : t is SwiftlyToolchain =>
455
- [ "stable" , "snapshot" ] . includes ( t . version . type )
456
- ) ;
452
+ return SwiftlyListAvailableResult . parse ( JSON . parse ( availableStdout ) )
453
+ . toolchains . filter ( ( t ) : t is AvailableToolchain =>
454
+ [ "system" , "stable" , "snapshot" ] . includes ( t . version . type )
455
+ )
456
+ . sort ( compareSwiftlyToolchain ) ;
457
457
} catch ( error ) {
458
458
logger ?. error ( `Failed to retrieve available Swiftly toolchains: ${ error } ` ) ;
459
459
return [ ] ;
@@ -878,3 +878,36 @@ export function checkForSwiftlyInstallation(contextKeys: ContextKeys, logger: Sw
878
878
} ) ;
879
879
} ) ;
880
880
}
881
+
882
+ function compareSwiftlyToolchain ( lhs : AvailableToolchain , rhs : AvailableToolchain ) : number {
883
+ return compareSwiftlyToolchainVersion ( lhs . version , rhs . version ) ;
884
+ }
885
+
886
+ function compareSwiftlyToolchainVersion ( lhs : ToolchainVersion , rhs : ToolchainVersion ) : number {
887
+ switch ( lhs . type ) {
888
+ case "system" : {
889
+ if ( rhs . type === "system" ) {
890
+ return lhs . name . localeCompare ( rhs . name ) ;
891
+ }
892
+ return - 1 ;
893
+ }
894
+ case "stable" : {
895
+ if ( rhs . type === "stable" ) {
896
+ const lhsVersion = new Version ( lhs . major , lhs . minor , lhs . patch ) ;
897
+ const rhsVersion = new Version ( rhs . major , rhs . minor , rhs . patch ) ;
898
+ return rhsVersion . compare ( lhsVersion ) ;
899
+ }
900
+ if ( rhs . type === "system" ) {
901
+ return 1 ;
902
+ }
903
+ return - 1 ;
904
+ }
905
+ case "snapshot" :
906
+ if ( rhs . type === "snapshot" ) {
907
+ const lhsDate = new Date ( lhs . date ) ;
908
+ const rhsDate = new Date ( rhs . date ) ;
909
+ return rhsDate . getTime ( ) - lhsDate . getTime ( ) ;
910
+ }
911
+ return 1 ;
912
+ }
913
+ }
0 commit comments