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 "rules_rust/crate_universe/test_data/crate_indexes/lazy_static/cargo_home", 71 ), 72 ); 73 74 let index = CrateIndexLookup::Http( 75 crates_index::SparseIndex::from_url("sparse+https://index.crates.io/").unwrap(), 76 ); 77 let source_info = index 78 .get_source_info(&cargo_lock::Package { 79 name: "lazy_static".parse().unwrap(), 80 version: Version::parse("1.4.0").unwrap(), 81 source: None, 82 checksum: None, 83 dependencies: Vec::new(), 84 replace: None, 85 }) 86 .unwrap() 87 .unwrap(); 88 assert_eq!( 89 source_info.url, 90 "https://crates.io/api/v1/crates/lazy_static/1.4.0/download" 91 ); 92 assert_eq!( 93 source_info.sha256, 94 "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 95 ); 96 } 97 { 98 let _e = EnvVarResetter::set("CARGO_HOME", runfiles.rlocation("rules_rust/crate_universe/test_data/crate_indexes/rewritten_lazy_static/cargo_home")); 99 100 let index = CrateIndexLookup::Http( 101 crates_index::SparseIndex::from_url("sparse+https://index.crates.io/").unwrap(), 102 ); 103 let source_info = index 104 .get_source_info(&cargo_lock::Package { 105 name: "lazy_static".parse().unwrap(), 106 version: Version::parse("1.4.0").unwrap(), 107 source: None, 108 checksum: None, 109 dependencies: Vec::new(), 110 replace: None, 111 }) 112 .unwrap() 113 .unwrap(); 114 assert_eq!( 115 source_info.url, 116 "https://some-mirror.com/api/v1/crates/lazy_static/1.4.0/download" 117 ); 118 assert_eq!( 119 source_info.sha256, 120 "fffffffffbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 121 ); 122 } 123 } 124 125 struct EnvVarResetter { 126 key: OsString, 127 value: Option<OsString>, 128 } 129 130 impl EnvVarResetter { set<K: Into<OsString>, V: Into<OsString>>(key: K, value: V) -> EnvVarResetter131 fn set<K: Into<OsString>, V: Into<OsString>>(key: K, value: V) -> EnvVarResetter { 132 let key = key.into(); 133 let value = value.into(); 134 let old_value = std::env::var_os(&key); 135 136 std::env::set_var(&key, value); 137 138 EnvVarResetter { 139 key, 140 value: old_value, 141 } 142 } 143 } 144 145 impl Drop for EnvVarResetter { drop(&mut self)146 fn drop(&mut self) { 147 if let Some(old_value) = self.value.as_ref() { 148 std::env::set_var(&self.key, old_value); 149 } else { 150 std::env::remove_var(&self.key); 151 } 152 } 153 } 154 } 155