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