@@ -31,91 +31,77 @@ import { ExecFileError, execFile, execFileStreamOutput } from "../utilities/util
3131import { Version } from "../utilities/version" ;
3232import { SwiftlyConfig } from "./ToolchainVersion" ;
3333
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 ( ) ,
6737} ) ;
38+ export type SystemVersion = z . infer < typeof SystemVersion > ;
6839
6940const StableVersion = z . object ( {
41+ type : z . literal ( "stable" ) ,
42+ name : z . string ( ) ,
43+
7044 major : z . number ( ) ,
7145 minor : z . number ( ) ,
7246 patch : z . number ( ) ,
73- name : z . string ( ) ,
74- type : z . literal ( "stable" ) ,
7547} ) ;
76-
7748export type StableVersion = z . infer < typeof StableVersion > ;
7849
7950const 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 ( ) ) ,
8256 branch : z . string ( ) ,
8357 date : z . string ( ) ,
84- name : z . string ( ) ,
85- type : z . literal ( "snapshot" ) ,
8658} ) ;
87-
8859export type SnapshotVersion = z . infer < typeof SnapshotVersion > ;
8960
90- export interface SwiftlyToolchain {
61+ export type ToolchainVersion = SystemVersion | StableVersion | SnapshotVersion ;
62+
63+ export interface AvailableToolchain {
9164 inUse : boolean ;
9265 installed : boolean ;
9366 isDefault : boolean ;
94- version : StableVersion | SnapshotVersion ;
67+ version : ToolchainVersion ;
9568}
9669
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+ ) ,
10284} ) ;
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- }
11085
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+ } ) ;
116102
117- const ListAvailableResult = z . object ( {
118- toolchains : z . array ( AvailableToolchain ) ,
103+ const InUseVersionResult = z . object ( {
104+ version : z . string ( ) ,
119105} ) ;
120106
121107export interface SwiftlyProgressData {
@@ -228,6 +214,8 @@ export class Swiftly {
228214 /**
229215 * Finds the list of toolchains installed via Swiftly.
230216 *
217+ * Toolchains will be sorted by version number in descending order.
218+ *
231219 * @returns an array of toolchain version names.
232220 */
233221 public static async list ( logger ?: SwiftLogger ) : Promise < string [ ] > {
@@ -250,10 +238,13 @@ export class Swiftly {
250238 private static async listUsingJSONFormat ( logger ?: SwiftLogger ) : Promise < string [ ] > {
251239 try {
252240 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 ) ;
257248 } catch ( error ) {
258249 logger ?. error ( `Failed to retrieve Swiftly installations: ${ error } ` ) ;
259250 return [ ] ;
@@ -274,8 +265,14 @@ export class Swiftly {
274265 if ( ! Array . isArray ( installedToolchains ) ) {
275266 return [ ] ;
276267 }
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 ) )
279276 ) ;
280277 } catch ( error ) {
281278 logger ?. error ( `Failed to retrieve Swiftly installations: ${ error } ` ) ;
@@ -421,14 +418,16 @@ export class Swiftly {
421418 /**
422419 * Lists all toolchains available for installation from swiftly.
423420 *
421+ * Toolchains will be sorted by version number in descending order.
422+ *
424423 * @param branch Optional branch to filter available toolchains (e.g., "main" for snapshots).
425424 * @param logger Optional logger for error reporting.
426425 * @returns Array of available toolchains.
427426 */
428427 public static async listAvailable (
429428 branch ?: string ,
430429 logger ?: SwiftLogger
431- ) : Promise < SwiftlyToolchain [ ] > {
430+ ) : Promise < AvailableToolchain [ ] > {
432431 if ( ! this . isSupported ( ) ) {
433432 return [ ] ;
434433 }
@@ -450,10 +449,11 @@ export class Swiftly {
450449 args . push ( branch ) ;
451450 }
452451 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 ) ;
457457 } catch ( error ) {
458458 logger ?. error ( `Failed to retrieve available Swiftly toolchains: ${ error } ` ) ;
459459 return [ ] ;
@@ -878,3 +878,36 @@ export function checkForSwiftlyInstallation(contextKeys: ContextKeys, logger: Sw
878878 } ) ;
879879 } ) ;
880880}
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