crate-seq-registry
Overview
crate-seq-registry is a blocking HTTP client for the crates.io v1 API. It handles metadata fetching, version existence checks, index polling, cargo publish subprocess execution, and exponential backoff retries.
CratesIoClient — client.rs
Construction
pub struct CratesIoClient {
inner: reqwest::blocking::Client,
base_url: String,
}
impl CratesIoClient {
/// production client with crates.io ToS-compliant User-Agent.
pub fn new(crate_version: &str) -> Result<Self, Error>;
/// test client redirected to a mock server.
pub fn with_base_url(crate_version: &str, base_url: &str) -> Result<Self, Error>;
}The User-Agent is "crate-seq/{version} (https://github.com/floppypancake/crate-seq)", which is required by crates.io Terms of Service.
fetch_crate_metadata
pub fn fetch_crate_metadata(
&self, crate_name: &str
) -> Result<Option<CrateMetadata>, Error>Hits GET {base_url}/api/v1/crates/{crate_name}. Returns None on 404 (crate doesn't exist). Deserializes the response into CrateMetadata, silently skipping entries whose num field is not valid SemVer.
check_version_exists
pub fn check_version_exists(
&self, crate_name: &str, version: &semver::Version
) -> Result<bool, Error>Delegates to fetch_crate_metadata and checks if any version matches. Returns false if the crate doesn't exist at all.
wait_for_index
pub fn wait_for_index(
&self, crate_name: &str, version: &semver::Version, backoff: &BackoffConfig
) -> Result<(), Error>Polls check_version_exists with the same backoff timing as publish retries. Returns Ok(()) once the version appears. Returns Error::IndexTimeout after backoff.max_retries attempts. Used between workspace tiers to wait for index propagation before publishing dependent crates.
Publish — publish.rs
PublishOutcome
pub enum PublishOutcome {
Success,
AlreadyPublished,
RateLimited,
Failed(String),
}run_cargo_publish
pub fn run_cargo_publish(
dir: &Path, token: Option<&str>
) -> Result<PublishOutcome, Error>Runs cargo publish --allow-dirty in dir, optionally passing --token. Captures stderr and classifies the outcome:
- Exit success →
Success - Stderr contains
"already exists"or"already uploaded"→AlreadyPublished - Stderr contains
"429","rate limit", or"too many requests"(case-insensitive) →RateLimited - Otherwise →
Failed(stderr)
Backoff — backoff.rs
BackoffConfig
pub struct BackoffConfig {
pub base_ms: u64, // default: 1000
pub cap_ms: u64, // default: 60000
pub max_retries: u32, // default: 5
pub jitter_max_ms: u64, // default: 1000
}Implements Default with the values shown above.
backoff_publish
pub fn backoff_publish(
dir: &Path, token: Option<&str>, config: &BackoffConfig
) -> Result<PublishOutcome, Error>Wraps run_cargo_publish in an exponential backoff retry loop:
- Success or AlreadyPublished → returns
Ok(Success)immediately - Failed → returns
Ok(Failed(...))immediately (no retry) - RateLimited → sleeps and retries up to
max_retriestimes - If all retries exhausted → returns
Ok(RateLimited)
Delay calculation
min(cap_ms, base_ms × 2^attempt) + jitterUses overflow-safe arithmetic. Jitter is deterministic — derived from a Knuth multiplicative hash of the attempt index, avoiding a random crate dependency.
Types — types.rs
pub struct CrateVersionInfo {
pub version: semver::Version,
pub yanked: bool,
}
pub struct CrateMetadata {
pub name: String,
pub versions: Vec<CrateVersionInfo>,
}CrateVersionInfo derives serde::Deserialize for JSON parsing from the crates.io API response.
Error type — error.rs
#[derive(Debug, thiserror::Error)]
pub enum Error {
Http(#[from] reqwest::Error),
Json(#[from] serde_json::Error),
Subprocess(std::io::Error), // not #[from] — manual construction
IndexTimeout { crate_name: String, version: semver::Version },
}Note: Subprocess wraps std::io::Error but is not #[from] because std::io::Error is already claimed by the snapshot crate in the unified error type. It is constructed manually.
Source files
| File | Responsibility |
|---|---|
client.rs |
HTTP client construction, metadata fetch, version check, index polling |
publish.rs |
cargo publish subprocess execution and outcome classification |
backoff.rs |
Exponential backoff retry loop with deterministic jitter |
types.rs |
Public data types (CrateMetadata, CrateVersionInfo) |
error.rs |
Crate error enum |
lib.rs |
Module declarations and re-exports |