1+ use crate :: client:: client:: Client ;
2+ use crate :: client:: errors:: { HttpError , IoError } ;
13use crate :: {
2- is_stale, Document , IsStaleError , ResourceFile , RetrievalLocation , RetrievedDocument ,
3- RustReleasesClient ,
4+ is_stale, ClientError , Document , IsStaleError , ResourceFile , RetrievalLocation ,
5+ RetrievedDocument , RustReleasesClient ,
46} ;
57use std:: fs;
68use std:: io:: { self , BufReader , BufWriter , Read , Write } ;
@@ -9,61 +11,7 @@ use std::time::Duration;
911
1012const DEFAULT_MEMORY_SIZE : usize = 4096 ;
1113
12- /// A list of errors which may be produced by [`CachedClient::fetch`].
13- #[ derive( Debug , thiserror:: Error ) ]
14- #[ non_exhaustive]
15- pub enum CachedClientError {
16- /// Returned if the fetched file was empty.
17- #[ error( "Received empty file" ) ]
18- EmptyFile ,
19-
20- /// Returned if the HTTP client could not fetch an item
21- #[ error( transparent) ]
22- Http ( #[ from] HttpError ) ,
23-
24- /// Returned in case of an `std::io::Error`.
25- #[ error( transparent) ]
26- Io ( #[ from] IoError ) ,
27-
28- /// Returned in case it wasn't possible to check whether the cache file is
29- /// stale or not.
30- #[ error( transparent) ]
31- IsStale ( #[ from] IsStaleError ) ,
32- }
33-
34- #[ derive( Debug , thiserror:: Error ) ]
35- pub enum IoError {
36- #[ error( "I/O error: {error}{}" , format!( " at '{}'" , . path. display( ) ) ) ]
37- Inaccessible { error : io:: Error , path : PathBuf } ,
38-
39- #[ error( "I/O error: path at '{path}' is a file, but expected a directory" ) ]
40- IsFile { path : PathBuf } ,
41-
42- #[ error( "I/O error: {error}" ) ]
43- Auxiliary { error : io:: Error } ,
44- }
45-
46- impl IoError {
47- pub fn auxiliary ( error : io:: Error ) -> Self {
48- Self :: Auxiliary { error }
49- }
50-
51- pub fn inaccessible ( error : io:: Error , path : PathBuf ) -> Self {
52- Self :: Inaccessible { error, path }
53- }
54-
55- pub fn is_file ( path : PathBuf ) -> Self {
56- Self :: IsFile { path }
57- }
58- }
59-
60- /// An error which is returned for a fault which occurred during processing of an HTTP request.
61- #[ derive( Debug , thiserror:: Error ) ]
62- #[ error( "HTTP error: {error}" ) ]
63- pub struct HttpError {
64- // We box the error since it can be very large.
65- error : Box < ureq:: Error > ,
66- }
14+ const DEFAULT_TIMEOUT : Duration = Duration :: from_secs ( 150 ) ;
6715
6816/// The client to download and cache rust releases.
6917///
@@ -99,34 +47,34 @@ impl RustReleasesClient for CachedClient {
9947 type Error = CachedClientError ;
10048
10149 fn fetch ( & self , resource : ResourceFile ) -> Result < RetrievedDocument , Self :: Error > {
102- let manifest_path = self . cache_folder . join ( resource. name ( ) ) ;
103- let exists = manifest_path . exists ( ) ;
50+ let path = self . cache_folder . join ( resource. name ( ) ) ;
51+ let exists = path . exists ( ) ;
10452
10553 // Returned the cached document if it exists and is not stale
106- if exists && !is_stale ( & manifest_path , self . cache_timeout ) ? {
107- let buffer = read_from_path ( & manifest_path ) ?;
54+ if exists && !is_stale ( & path , self . cache_timeout ) ? {
55+ let buffer = read_from_path ( & path ) ?;
10856 let document = Document :: new ( buffer) ;
10957
11058 return Ok ( RetrievedDocument :: new (
11159 document,
112- RetrievalLocation :: Cache ( manifest_path ) ,
60+ RetrievalLocation :: Cache ( path ) ,
11361 ) ) ;
11462 }
11563
11664 // Ensure we have a place to put the cached document.
11765 if !exists {
118- setup_cache_folder ( & manifest_path ) ?;
66+ setup_cache_folder ( & path ) ?;
11967 }
12068
121- let mut reader = fetch_file ( resource. url ( ) ) ?;
69+ let client = Client :: new ( DEFAULT_TIMEOUT ) ;
70+ let mut retrieved = client. fetch ( resource) . map_err ( CachedClientError :: from) ?;
71+
72+ let document = retrieved. mut_document ( ) ;
12273
12374 // write to memory
124- let document = write_document_and_cache ( & mut reader , & manifest_path ) ?;
75+ write_document_and_cache ( document , & path ) ?;
12576
126- Ok ( RetrievedDocument :: new (
127- document,
128- RetrievalLocation :: RemoteUrl ( resource. url . to_string ( ) ) ,
129- ) )
77+ Ok ( retrieved)
13078 }
13179}
13280
@@ -169,44 +117,49 @@ fn setup_cache_folder(manifest_path: &Path) -> Result<(), CachedClientError> {
169117 Ok ( ( ) )
170118}
171119
172- fn fetch_file ( url : & str ) -> Result < Box < dyn Read + Send + Sync > , CachedClientError > {
173- let config = ureq:: Agent :: config_builder ( )
174- . user_agent ( "rust-releases (github.com/foresterre/rust-releases/issues)" )
175- . proxy ( ureq:: Proxy :: try_from_env ( ) )
176- . build ( ) ;
177-
178- let agent = config. new_agent ( ) ;
179-
180- let response = agent. get ( url) . call ( ) . map_err ( |err| HttpError {
181- error : Box :: new ( err) ,
182- } ) ?;
183-
184- let reader = Box :: new ( response. into_body ( ) . into_reader ( ) ) ;
185-
186- Ok ( reader)
187- }
188-
189120fn write_document_and_cache (
190- reader : & mut Box < dyn Read + Send + Sync > ,
121+ document : & mut Document ,
191122 file_path : & Path ,
192- ) -> Result < Document , CachedClientError > {
193- let mut buffer = Vec :: with_capacity ( DEFAULT_MEMORY_SIZE ) ;
194-
195- let bytes_read = reader
196- . read_to_end ( & mut buffer)
197- . map_err ( |err| IoError :: inaccessible ( err, file_path. to_path_buf ( ) ) ) ?;
198-
199- if bytes_read == 0 {
200- return Err ( CachedClientError :: EmptyFile ) ;
201- }
202-
123+ ) -> Result < ( ) , CachedClientError > {
203124 let mut file = fs:: File :: create ( file_path)
204125 . map_err ( |err| IoError :: inaccessible ( err, file_path. to_path_buf ( ) ) ) ?;
205126
206127 let mut writer = BufWriter :: new ( & mut file) ;
207128 writer
208- . write_all ( & buffer)
129+ . write_all ( document . buffer ( ) )
209130 . map_err ( |err| IoError :: inaccessible ( err, file_path. to_path_buf ( ) ) ) ?;
210131
211- Ok ( Document :: new ( buffer) )
132+ Ok ( ( ) )
133+ }
134+
135+ /// A list of errors which may be produced by [`CachedClient::fetch`].
136+ #[ derive( Debug , thiserror:: Error ) ]
137+ #[ non_exhaustive]
138+ pub enum CachedClientError {
139+ /// Returned if the fetched file was empty.
140+ #[ error( "Received empty file" ) ]
141+ EmptyFile ,
142+
143+ /// Returned if the HTTP client could not fetch an item
144+ #[ error( transparent) ]
145+ Http ( #[ from] HttpError ) ,
146+
147+ /// Returned in case of an `std::io::Error`.
148+ #[ error( transparent) ]
149+ Io ( #[ from] IoError ) ,
150+
151+ /// Returned in case it wasn't possible to check whether the cache file is
152+ /// stale or not.
153+ #[ error( transparent) ]
154+ IsStale ( #[ from] IsStaleError ) ,
155+ }
156+
157+ impl From < ClientError > for CachedClientError {
158+ fn from ( err : ClientError ) -> Self {
159+ match err {
160+ ClientError :: Empty => CachedClientError :: EmptyFile ,
161+ ClientError :: Http ( err) => CachedClientError :: Http ( err) ,
162+ ClientError :: Io ( err) => CachedClientError :: Io ( err) ,
163+ }
164+ }
212165}
0 commit comments