import json
import sys
import urllib.error
import urllib.request
TIMEOUT_SECONDS = 15
def index_path(crate: str) -> str:
name = crate.lower()
if len(name) == 1:
return f"1/{name}"
if len(name) == 2:
return f"2/{name}"
if len(name) == 3:
return f"3/{name[0]}/{name}"
return f"{name[0:2]}/{name[2:4]}/{name}"
def published_versions(crate: str) -> list[dict]:
url = f"https://index.crates.io/{index_path(crate)}"
with urllib.request.urlopen(url, timeout=TIMEOUT_SECONDS) as resp:
body = resp.read().decode("utf-8")
return [json.loads(line) for line in body.splitlines() if line.strip()]
def main() -> int:
if len(sys.argv) != 3:
print(f"usage: {sys.argv[0]} <crate> <version>", file=sys.stderr)
return 2
crate, version = sys.argv[1], sys.argv[2]
try:
versions = published_versions(crate)
except urllib.error.HTTPError as e:
if e.code == 404:
print(f"{crate} is not on crates.io at all", file=sys.stderr)
return 1
print(f"crates.io index lookup failed for {crate}: {e}", file=sys.stderr)
return 2
except Exception as e: print(f"crates.io index lookup failed for {crate}: {e}", file=sys.stderr)
return 2
for record in versions:
if record.get("vers") == version:
if record.get("yanked"):
print(f"{crate} {version} is published but YANKED", file=sys.stderr)
return 1
print(f"{crate} {version} is published")
return 0
print(f"{crate} {version} is not published yet", file=sys.stderr)
return 1
if __name__ == "__main__":
sys.exit(main())