• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2024 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use crates_index::{http, Crate, SparseIndex};
16 use std::{cell::RefCell, collections::HashSet};
17 
18 use crate::Error;
19 
20 pub struct CratesIoIndex {
21     fetcher: Box<dyn CratesIoFetcher>,
22 }
23 
24 impl CratesIoIndex {
new() -> Result<CratesIoIndex, Error>25     pub fn new() -> Result<CratesIoIndex, Error> {
26         Ok(CratesIoIndex {
27             fetcher: Box::new(OnlineFetcher {
28                 index: crates_index::SparseIndex::new_cargo_default()?,
29                 agent: ureq::Agent::new_with_defaults(),
30                 fetched: RefCell::new(HashSet::new()),
31             }),
32         })
33     }
new_offline() -> Result<CratesIoIndex, Error>34     pub fn new_offline() -> Result<CratesIoIndex, Error> {
35         Ok(CratesIoIndex {
36             fetcher: Box::new(OfflineFetcher {
37                 index: crates_index::SparseIndex::new_cargo_default()?,
38             }),
39         })
40     }
get_crate(&self, crate_name: impl AsRef<str>) -> Result<Crate, Error>41     pub fn get_crate(&self, crate_name: impl AsRef<str>) -> Result<Crate, Error> {
42         self.fetcher.fetch(crate_name.as_ref())
43     }
44 }
45 
46 pub trait CratesIoFetcher {
fetch(&self, crate_name: &str) -> Result<Crate, Error>47     fn fetch(&self, crate_name: &str) -> Result<Crate, Error>;
48 }
49 
50 struct OnlineFetcher {
51     index: SparseIndex,
52     agent: ureq::Agent,
53     // Keep track of crates we have fetched, to avoid fetching them multiple times.
54     fetched: RefCell<HashSet<String>>,
55 }
56 
57 struct OfflineFetcher {
58     index: SparseIndex,
59 }
60 
61 impl CratesIoFetcher for OnlineFetcher {
fetch(&self, crate_name: &str) -> Result<Crate, Error>62     fn fetch(&self, crate_name: &str) -> Result<Crate, Error> {
63         let mut fetched = self.fetched.borrow_mut();
64         if fetched.contains(crate_name) {
65             return Ok(self.index.crate_from_cache(crate_name.as_ref())?);
66         }
67 
68         let request = self
69             .index
70             .make_cache_request(crate_name)?
71             .version(ureq::http::Version::HTTP_11)
72             .body(())?;
73 
74         let response = self.agent.run(request)?;
75         let (parts, mut body) = response.into_parts();
76         let response = http::Response::from_parts(parts, body.read_to_vec()?);
77         let response = self
78             .index
79             .parse_cache_response(crate_name, response, true)?
80             .ok_or(Error::CrateNotFound(crate_name.to_string()))?;
81 
82         fetched.insert(crate_name.to_string());
83         Ok(response)
84     }
85 }
86 
87 impl CratesIoFetcher for OfflineFetcher {
fetch(&self, crate_name: &str) -> Result<Crate, Error>88     fn fetch(&self, crate_name: &str) -> Result<Crate, Error> {
89         Ok(self.index.crate_from_cache(crate_name.as_ref())?)
90     }
91 }
92