22using System . ComponentModel . Composition ;
33using System . Globalization ;
44using System . IO ;
5+ using System . IO . Compression ;
56using System . Linq ;
67using System . Net . Http ;
78using System . Reflection ;
89using System . Threading ;
910using System . Threading . Tasks ;
1011using KS . RustAnalyzer . TestAdapter . Common ;
12+ using Microsoft . VisualStudio . Shell ;
13+ using Microsoft . Win32 ;
1114
1215namespace KS . RustAnalyzer . Infrastructure ;
1316
@@ -23,8 +26,8 @@ public interface IRADownloaderService
2326public class RADownloaderService : IRADownloaderService
2427{
2528 public const string LatestInPackageRAVersion = "2024-01-08" ;
26-
2729 public const string RAVersionFormat = "yyyy-MM-dd" ;
30+ private const string InstalledRAVersionKey = "InstalledRAVersion" ;
2831 private readonly IRegistrySettingsService _regSettings ;
2932 private readonly TL _tl ;
3033
@@ -39,16 +42,44 @@ public RADownloaderService(IRegistrySettingsService regSettings, [Import] ITelem
3942 } ;
4043 }
4144
42- public Task DownloadLatestRAAsync ( )
45+ public async Task DownloadLatestRAAsync ( )
4346 {
47+ if ( ! IsDownloadEnabled ( ) )
48+ {
49+ return ;
50+ }
51+
4452 _tl . L . WriteLine ( "Initiating download of RA..." ) ;
45- return Task . CompletedTask ;
53+ try
54+ {
55+ var latestRel = await GetLatestRAReleaseRedirectUriAsync ( ) ;
56+ string installedVer = await GetInstalledVersionAsync ( ) ;
57+ if ( latestRel != null && installedVer . CompareTo ( latestRel ? . Version ) >= 0 )
58+ {
59+ _tl . L . WriteLine ( $ "Not going to download RA. Installed = { installedVer } , Latest = { latestRel ? . Uri } .") ;
60+ _tl . T . TrackEvent ( "RADS.RAUpToDate" , ( "Installed" , installedVer ) , ( "Latest" , latestRel ? . Uri . ToString ( ) ) ) ;
61+ return ;
62+ }
63+
64+ using var response = await DownloadAsync ( latestRel ) ;
65+
66+ using var zipStream = await response . Content . ReadAsStreamAsync ( ) ;
67+ Install ( zipStream , latestRel ? . Version ) ;
68+
69+ await CommitAsync ( latestRel ) ;
70+ _tl . T . TrackEvent ( "RADS.RAInstalled" , ( "Installed" , installedVer ) ) ;
71+ }
72+ catch ( Exception ex )
73+ {
74+ _tl . L . WriteError ( $ "Download failed. StatusCode { ex } ") ;
75+ _tl . T . TrackException ( ex ) ;
76+ throw ;
77+ }
4678 }
4779
48- public Task < PathEx > GetRustAnalyzerExePathAsync ( )
80+ public async Task < PathEx > GetRustAnalyzerExePathAsync ( )
4981 {
50- var path = ( PathEx ) Path . Combine ( Path . GetDirectoryName ( Assembly . GetExecutingAssembly ( ) . Location ) , $ "rust-analyzer.{ LatestInPackageRAVersion } .exe") ;
51- return path . ToTask ( ) ;
82+ return GetVersionedRAExePath ( await GetInstalledVersionAsync ( ) ) ;
5283 }
5384
5485 public static async Task < ( Uri Uri , string Version ) ? > GetLatestRAReleaseRedirectUriAsync ( )
@@ -60,14 +91,91 @@ public Task<PathEx> GetRustAnalyzerExePathAsync()
6091 var latestRelVersion = latestRelUri . Segments [ latestRelUri . Segments . Length - 1 ] ;
6192 var latestRelDate = DateTime . ParseExact ( latestRelVersion , RAVersionFormat , CultureInfo . InvariantCulture ) ;
6293
63- return ( Uri : latestRelUri , Version : latestRelDate . ToString ( RAVersionFormat , CultureInfo . InvariantCulture ) ) ;
94+ return ( Uri : new Uri ( $ "https://github.com/rust-lang/rust-analyzer/releases/download/{ latestRelVersion } /rust-analyzer-x86_64-pc-windows-msvc.zip") ,
95+ Version : latestRelDate . ToString ( RAVersionFormat , CultureInfo . InvariantCulture ) ) ;
6496 }
6597 catch
6698 {
6799 return null ;
68100 }
69101 }
70102
103+ private bool IsDownloadEnabled ( )
104+ {
105+ var id = Environment . ExpandEnvironmentVariables ( "%USERNAME%@%COMPUTERNAME%.%USERDOMAIN%" ) ;
106+ return id ? . Equals ( "[email protected] " , StringComparison . OrdinalIgnoreCase ) ?? false ; 107+ }
108+
109+ private PathEx GetVersionedRAExePath ( string version )
110+ {
111+ return GetRAFolder ( version ) + ( PathEx ) $ "rust-analyzer.exe";
112+ }
113+
114+ private async Task < HttpResponseMessage > DownloadAsync ( ( Uri Uri , string Version ) ? latestRel )
115+ {
116+ _tl . L . WriteLine ( $ "Downloading RA v{ latestRel ? . Uri } .") ;
117+ var response = await new HttpClient ( ) . GetAsync ( latestRel ? . Uri ) ;
118+ if ( ! response . IsSuccessStatusCode )
119+ {
120+ _tl . L . WriteError ( $ "Download failed. StatusCode { response . StatusCode } .") ;
121+ _tl . T . TrackEvent ( "RADS.RADownloadFailed" , ( "StatusCode" , response . StatusCode . ToString ( ) ) ) ;
122+ throw new Exception ( $ "RADS.RADownloadFailed. { response . StatusCode } .") ;
123+ }
124+
125+ return response ;
126+ }
127+
128+ private async Task CommitAsync ( ( Uri Uri , string Version ) ? latestRel )
129+ {
130+ await ThreadHelper . JoinableTaskFactory . SwitchToMainThreadAsync ( ) ;
131+ if ( ! _regSettings . GetPackageRegistryRoot ( out var regRoot ) )
132+ {
133+ _tl . L . WriteError ( $ "GetPackageRegistryRoot failed.") ;
134+ throw new Exception ( $ "GetPackageRegistryRoot failed.") ;
135+ }
136+
137+ Registry . SetValue ( regRoot , InstalledRAVersionKey , latestRel ? . Version ) ;
138+ _tl . L . WriteLine ( $ "Committed RA installation.") ;
139+ }
140+
141+ private void Install ( Stream zipStream , string downloadedVersion )
142+ {
143+ _tl . L . WriteLine ( $ "Installing RA v{ downloadedVersion } ...") ;
144+ var raFolder = GetRAFolder ( downloadedVersion ) ;
145+ Directory . CreateDirectory ( raFolder ) ;
146+ using var zip = new ZipArchive ( zipStream ) ;
147+ foreach ( var entry in zip . Entries )
148+ {
149+ var dstFile = raFolder + entry . FullName ;
150+ entry . ExtractToFile ( dstFile , true ) ;
151+ _tl . L . WriteLine ( $ "... Installing { dstFile } ") ;
152+ }
153+ }
154+
155+ private async Task < string > GetInstalledVersionAsync ( )
156+ {
157+ await ThreadHelper . JoinableTaskFactory . SwitchToMainThreadAsync ( ) ;
158+
159+ var installedRAVersion = LatestInPackageRAVersion ;
160+ if ( _regSettings . GetPackageRegistryRoot ( out var regRoot ) )
161+ {
162+ installedRAVersion = Registry . GetValue ( regRoot , InstalledRAVersionKey , null ) as string ;
163+ }
164+
165+ installedRAVersion ??= LatestInPackageRAVersion ;
166+ if ( GetVersionedRAExePath ( installedRAVersion ) . FileExists ( ) )
167+ {
168+ return installedRAVersion ;
169+ }
170+
171+ return LatestInPackageRAVersion ;
172+ }
173+
174+ private static PathEx GetRAFolder ( string version )
175+ {
176+ return ( PathEx ) Path . GetDirectoryName ( Assembly . GetExecutingAssembly ( ) . Location ) + ( PathEx ) version ;
177+ }
178+
71179 private static async Task < Uri > GetRedirectedUrlAsync ( Uri uri , CancellationToken cancellationToken = default )
72180 {
73181 using var client = new HttpClient ( new HttpClientHandler { AllowAutoRedirect = false , } , true ) ;
0 commit comments