xref: /aosp_15_r20/external/bazelbuild-rules_rust/crate_universe/src/splicing/crate_index_lookup.rs (revision d4726bddaa87cc4778e7472feed243fa4b6c267f)
1 use crate::splicing::SourceInfo;
2 use anyhow::{Context, Result};
3 use crates_index::IndexConfig;
4 use hex::ToHex;
5 
6 pub(crate) enum CrateIndexLookup {
7     Git(crates_index::GitIndex),
8     Http(crates_index::SparseIndex),
9 }
10 
11 impl CrateIndexLookup {
get_source_info(&self, pkg: &cargo_lock::Package) -> Result<Option<SourceInfo>>12     pub(crate) fn get_source_info(&self, pkg: &cargo_lock::Package) -> Result<Option<SourceInfo>> {
13         let index_config = self
14             .index_config()
15             .context("Failed to get crate index config")?;
16         let crate_ = match self {
17             // The crates we care about should all be in the cache already,
18             // because `cargo metadata` ran which should have fetched them.
19             Self::Http(index) => Some(
20                 index
21                     .crate_from_cache(pkg.name.as_str())
22                     .with_context(|| format!("Failed to get crate from cache for {pkg:?}"))?,
23             ),
24             Self::Git(index) => index.crate_(pkg.name.as_str()),
25         };
26         let source_info = crate_.and_then(|crate_idx| {
27             crate_idx
28                 .versions()
29                 .iter()
30                 .find(|v| v.version() == pkg.version.to_string())
31                 .and_then(|v| {
32                     v.download_url(&index_config).map(|url| {
33                         let sha256 = pkg
34                             .checksum
35                             .as_ref()
36                             .and_then(|sum| sum.as_sha256().map(|sum| sum.encode_hex::<String>()))
37                             .unwrap_or_else(|| v.checksum().encode_hex::<String>());
38                         SourceInfo { url, sha256 }
39                     })
40                 })
41         });
42         Ok(source_info)
43     }
44 
45     #[allow(clippy::result_large_err)]
index_config(&self) -> Result<IndexConfig, crates_index::Error>46     fn index_config(&self) -> Result<IndexConfig, crates_index::Error> {
47         match self {
48             Self::Git(index) => index.index_config(),
49             Self::Http(index) => index.index_config(),
50         }
51     }
52 }
53 
54 #[cfg(test)]
55 mod test {
56     use crate::splicing::crate_index_lookup::CrateIndexLookup;
57     use semver::Version;
58     use std::ffi::OsString;
59 
60     // TODO: Avoid global state (env vars) in these tests.
61     // TODO: These should be separate tests methods but they have conflicting state.
62 
63     #[test]
sparse_index()64     fn sparse_index() {
65         let runfiles = runfiles::Runfiles::create().unwrap();
66         {
67             let _e = EnvVarResetter::set(
68                 "CARGO_HOME",
69                 runfiles::rlocation!(
70                     runfiles,
71                     "rules_rust/crate_universe/test_data/crate_indexes/lazy_static/cargo_home"
72                 ),
73             );
74 
75             let index = CrateIndexLookup::Http(
76                 crates_index::SparseIndex::from_url("sparse+https://index.crates.io/").unwrap(),
77             );
78             let source_info = index
79                 .get_source_info(&cargo_lock::Package {
80                     name: "lazy_static".parse().unwrap(),
81                     version: Version::parse("1.4.0").unwrap(),
82                     source: None,
83                     checksum: None,
84                     dependencies: Vec::new(),
85                     replace: None,
86                 })
87                 .unwrap()
88                 .unwrap();
89             assert_eq!(
90                 source_info.url,
91                 "https://crates.io/api/v1/crates/lazy_static/1.4.0/download"
92             );
93             assert_eq!(
94                 source_info.sha256,
95                 "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
96             );
97         }
98         {
99             let _e = EnvVarResetter::set("CARGO_HOME",
100                 runfiles::rlocation!(runfiles, "rules_rust/crate_universe/test_data/crate_indexes/rewritten_lazy_static/cargo_home"));
101 
102             let index = CrateIndexLookup::Http(
103                 crates_index::SparseIndex::from_url("sparse+https://index.crates.io/").unwrap(),
104             );
105             let source_info = index
106                 .get_source_info(&cargo_lock::Package {
107                     name: "lazy_static".parse().unwrap(),
108                     version: Version::parse("1.4.0").unwrap(),
109                     source: None,
110                     checksum: None,
111                     dependencies: Vec::new(),
112                     replace: None,
113                 })
114                 .unwrap()
115                 .unwrap();
116             assert_eq!(
117                 source_info.url,
118                 "https://some-mirror.com/api/v1/crates/lazy_static/1.4.0/download"
119             );
120             assert_eq!(
121                 source_info.sha256,
122                 "fffffffffbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
123             );
124         }
125     }
126 
127     struct EnvVarResetter {
128         key: OsString,
129         value: Option<OsString>,
130     }
131 
132     impl EnvVarResetter {
set<K: Into<OsString>, V: Into<OsString>>(key: K, value: V) -> EnvVarResetter133         fn set<K: Into<OsString>, V: Into<OsString>>(key: K, value: V) -> EnvVarResetter {
134             let key = key.into();
135             let value = value.into();
136             let old_value = std::env::var_os(&key);
137 
138             std::env::set_var(&key, value);
139 
140             EnvVarResetter {
141                 key,
142                 value: old_value,
143             }
144         }
145     }
146 
147     impl Drop for EnvVarResetter {
drop(&mut self)148         fn drop(&mut self) {
149             if let Some(old_value) = self.value.as_ref() {
150                 std::env::set_var(&self.key, old_value);
151             } else {
152                 std::env::remove_var(&self.key);
153             }
154         }
155     }
156 }
157