diff options
| author | seth <[email protected]> | 2024-09-08 23:39:48 -0400 |
|---|---|---|
| committer | seth <[email protected]> | 2024-09-13 17:03:00 -0400 |
| commit | cc183fccca73df619c78dd0ca2567ac547c56ad2 (patch) | |
| tree | a06a87049cd90e877e626b8ff31e27a373df8f39 /src/http.rs | |
feat: initial commit
Diffstat (limited to 'src/http.rs')
| -rw-r--r-- | src/http.rs | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/src/http.rs b/src/http.rs new file mode 100644 index 0000000..1b37d4c --- /dev/null +++ b/src/http.rs @@ -0,0 +1,77 @@ +/// HTTP interfaces +use crate::Error; + +use anyhow::Result; +use reqwest::{Method, StatusCode}; +use tracing::{event, instrument, Level}; + +// https://nix.dev/manual/nix/2.24/store/store-path +const STORE_DIRECTORY: &str = "/nix/store"; +const DIGEST_SIZE: usize = 32; + +pub type Client = reqwest::Client; + +pub trait Ext { + fn default() -> Self; + async fn has_store_path(&self, binary_cache_url: &str, store_path: &str) -> Result<bool>; +} + +impl Ext for Client { + /// Create a client with our user agent + fn default() -> Self { + Client::builder() + .user_agent(concat!( + env!("CARGO_PKG_NAME"), + "/", + env!("CARGO_PKG_VERSION") + )) + .build() + .unwrap() + } + + /// Check if a store path is available in the given binary cache + #[instrument(skip(self, binary_cache_url, store_path))] + async fn has_store_path(&self, binary_cache_url: &str, store_path: &str) -> Result<bool> { + let url = format!( + "{binary_cache_url}/{}.narinfo", + hash_from_store_path(store_path) + ); + + let request = self.request(Method::HEAD, url).build()?; + event!( + Level::TRACE, + "Checking for store path {store_path} in binary cache at {}", + request.url() + ); + let response = self.execute(request).await?; + + match response.status() { + StatusCode::OK => { + event!( + Level::TRACE, + "Found store path `{store_path}` in binary cache" + ); + Ok(true) + } + StatusCode::NOT_FOUND => { + event!( + Level::TRACE, + "Did not find store path `{store_path}` in binary cache" + ); + Ok(false) + } + status_code => Err(Error::HTTPFailed(status_code).into()), + } + } +} + +/// Strip the <hash> from /nix/store/<hash>-<name> +fn hash_from_store_path(store_path: &str) -> &str { + // Store paths will always start with the store directory, followed by a path separator. See + // the above link + let start_index = STORE_DIRECTORY.len() + 1; + // The next DIGEST_SIZE characters will then be the digest + let end_index = start_index + DIGEST_SIZE; + + &store_path[start_index..end_index] +} |
