<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" href="/webapp/favicon.png">
<title>
Cratery
</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<header style="position: sticky; top: 0;">
<nav class="bg-white border-gray-200 px-4 lg:px-6 py-2.5 dark:bg-gray-800">
<div class="flex flex-wrap justify-between items-center mx-auto max-w-screen-xl">
<a href="/webapp/index.html" class="flex items-center">
<picture>
<source srcset="./logo-white.svg" media="(prefers-color-scheme: dark)" />
<source srcset="./logo-black.svg" media="(prefers-color-scheme: light)" />
<img src="./logo-white.svg" class="mr-3 h-6 sm:h-9" style="min-width: 200px;" alt="Cratery Logo" />
</picture>
</a>
<div class="flex items-center lg:order-2">
<a id="link-admin" href="/webapp/admin.html" style="cursor: pointer;" class="text-gray-800 dark:text-white hover:bg-gray-50 focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-sm px-4 lg:px-5 py-2 lg:py-2.5 mr-2 dark:hover:bg-gray-700 focus:outline-none dark:focus:ring-gray-800">Admin</a>
<a id="link-account" href="/webapp/account.html" style="cursor: pointer;" class="text-gray-800 dark:text-white hover:bg-gray-50 focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-sm px-4 lg:px-5 py-2 lg:py-2.5 mr-2 dark:hover:bg-gray-700 focus:outline-none dark:focus:ring-gray-800">My Account</a>
<a onclick="doLogout()" style="cursor: pointer;" class="text-gray-800 dark:text-white hover:bg-gray-50 focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-sm px-4 lg:px-5 py-2 lg:py-2.5 mr-2 dark:hover:bg-gray-700 focus:outline-none dark:focus:ring-gray-800">Logout</a>
</div>
</div>
</nav>
</header>
<body onload="doPageLoad()" class="bg-white dark:bg-gray-800 content-center">
<section class="bg-white dark:bg-gray-900 max-w-screen-lg mx-auto">
<div class="py-8 lg:py-16 px-4">
<h3 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
<span id="meta-name"></span>
<a id="meta-name-link" style="display: inline-block;">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244" />
</svg>
</a>
</h3>
<div id = "meta-deprecation" class="p-4 mb-4 text-sm text-yellow-800 rounded-lg bg-yellow-50 dark:bg-gray-800 dark:text-yellow-300" style="display: none;" role="alert">
<span class="font-medium">This crate is marked as deprecated (all versions) and should not be used.</span>
</div>
<p id="meta-version" class="mb-3 font-normal text-gray-700 dark:text-gray-400"></p>
<p id="meta-description" class="mb-3 font-normal text-gray-700 dark:text-gray-400"></p>
<ul class="flex flex-wrap text-sm font-medium text-center text-gray-500 border-b border-gray-200 dark:border-gray-700 dark:text-gray-400">
<li class="me-2">
<a id="header-readme" class="inline-block p-4 text-blue-600 bg-gray-100 rounded-t-lg active dark:bg-gray-800 dark:text-blue-500" style="cursor: pointer;" onclick="onClickTab(0)">Readme</a>
</li>
<li class="me-2">
<a id="header-versions" class="inline-block p-4 rounded-t-lg hover:text-gray-600 hover:bg-gray-50 dark:hover:bg-gray-800 dark:hover:text-gray-300" style="cursor: pointer;" onclick="onClickTab(1)">Versions</a>
</li>
<li class="me-2">
<a id="header-features" class="inline-block p-4 rounded-t-lg hover:text-gray-600 hover:bg-gray-50 dark:hover:bg-gray-800 dark:hover:text-gray-300" style="cursor: pointer;" onclick="onClickTab(2)">Features</a>
</li>
<li class="me-2">
<a id="header-dependencies" class="inline-block p-4 rounded-t-lg hover:text-gray-600 hover:bg-gray-50 dark:hover:bg-gray-800 dark:hover:text-gray-300" style="cursor: pointer;" onclick="onClickTab(3)">
<span id="header-dependencies-warn" style="display: none; vertical-align: middle;">
<svg id="header-dependencies-warn-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="#B8860B" class="size-6" style="width: 20px; height: 20px;">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" />
</svg>
</span>
<span style="display: inline-block; vertical-align: middle;">Dependencies</span>
</a>
</li>
<li class="me-2">
<a id="header-docs" class="inline-block p-4 rounded-t-lg hover:text-gray-600 hover:bg-gray-50 dark:hover:bg-gray-800 dark:hover:text-gray-300" style="cursor: pointer;" onclick="onClickTab(4)">Documentation</a>
</li>
<li class="me-2">
<a id="header-admin" class="inline-block p-4 rounded-t-lg hover:text-gray-600 hover:bg-gray-50 dark:hover:bg-gray-800 dark:hover:text-gray-300" style="cursor: pointer;" onclick="onClickTab(5)">Settings</a>
</li>
</ul>
<div id="tab-readme" class="m-4 flex flex-row">
<div class="basis-3/4" style="width: 0">
<div id="tab-readme-content">
</div>
<div>
<h5 class="text-xl font-bold tracking-tight text-gray-900 dark:text-white">Stats overview</h5>
<div class="m-4 flex flex-row flex-wrap">
<div class="basis-1/3 mx-2">
<div class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
<h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
<span style="display: inline-block;">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3" />
</svg>
</span>
<span id="tab-readme-dl-total"></span>
</h5>
<p class="font-normal text-gray-700 dark:text-gray-400">Downloads all time</p>
</div>
</div>
<div class="basis-1/3 mx-2">
<div class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
<h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
<span>📦</span>
<span id="tab-readme-versions-count"></span>
</h5>
<p class="font-normal text-gray-700 dark:text-gray-400">Versions published</p>
</div>
</div>
</div>
<h5 class="text-xl font-bold tracking-tight text-gray-900 dark:text-white">Downloads over the last 90 days</h5>
<div>
<canvas id="tab-readme-dl-chart"></canvas>
</div>
</div>
</div>
<div id="tab-readme-props" class="basis-1/4 mx-2">
<h5 class="text-xl font-bold tracking-tight text-gray-900 dark:text-white">Metadata</h5>
<p id="meta-uploaded-on" class="ml-4 font-normal text-gray-700 dark:text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6" style="display: inline;">
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5m-9-6h.008v.008H12v-.008ZM12 15h.008v.008H12V15Zm0 2.25h.008v.008H12v-.008ZM9.75 15h.008v.008H9.75V15Zm0 2.25h.008v.008H9.75v-.008ZM7.5 15h.008v.008H7.5V15Zm0 2.25h.008v.008H7.5v-.008Zm6.75-4.5h.008v.008h-.008v-.008Zm0 2.25h.008v.008h-.008V15Zm0 2.25h.008v.008h-.008v-.008Zm2.25-4.5h.008v.008H16.5v-.008Zm0 2.25h.008v.008H16.5V15Z" />
</svg>
</p>
<a id="meta-uploaded-by" class="ml-4 font-normal text-gray-700 dark:text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6" style="display: inline;">
<path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
</svg>
</a>
<h5 class="text-xl font-bold tracking-tight text-gray-900 dark:text-white mt-8">Install</h5>
<p class="ml-4 text-xs font-normal text-gray-700 dark:text-gray-400">
Add the following line to your Cargo.toml:
</p>
<pre class="max-w-xs"><code id="meta-install" class="language-toml hljs"></code></pre>
<h5 class="text-xl font-bold tracking-tight text-gray-900 dark:text-white mt-8">Documentation</h5>
<div class="ml-4 font-normal text-gray-700 dark:text-gray-400">
<ul id="meta-docs" class="max-w-md space-y-1 text-gray-500 list-disc list-inside dark:text-gray-400"></ul>
</div>
<h5 class="text-xl font-bold tracking-tight text-gray-900 dark:text-white mt-8">Owners</h5>
</div>
</div>
<div id="tab-versions" class="m-4" style="display: none;">
</div>
<div id="tab-features" class="m-4" style="display: none;">
</div>
<div id="tab-dependencies" class="m-4" style="display: none;">
</div>
<div id="tab-docs" class="m-4" style="display: none;">
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th scope="col" class="px-6 py-3">
Version
</th>
<th scope="col" class="px-6 py-3">
Targets / Status
</th>
<th scope="col" class="px-6 py-3">
Actions
</th>
</tr>
</thead>
<tbody id="tab-docs-table">
</tbody>
</table>
</table>
</div>
<div id="tab-admin" class="m-4" style="display: none;">
<div id="tab-admin-owners" class="m-4">
<h5 class="text-xl font-bold tracking-tight text-gray-900 dark:text-white mt-8">Owners <button id="button-add-owner" type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-xs px-3 py-2 me-1 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">add</button></h5>
</div>
<div id="tab-admin-targets" class="m-4">
<h5 class="text-xl font-bold tracking-tight text-gray-900 dark:text-white mt-8">Targets <button id="button-add-target" type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-xs px-3 py-2 me-1 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">add</button></h5>
</div>
<div id="tab-admin-capabilities" class="m-4">
<h5 class="text-xl font-bold tracking-tight text-gray-900 dark:text-white mt-8">Required capabilities <button id="button-add-capability" type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-xs px-3 py-2 me-1 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">add</button></h5>
</div>
<div id="tab-admin-deprecation" class="m-4">
<h5 class="text-xl font-bold tracking-tight text-gray-900 dark:text-white mt-8">Deprecation</h5>
<p class="mb-3 text-gray-500 dark:text-gray-400">Deprecated crates will not be checked by the dependency analyzer and will be marked with a warning in the web interface.</p>
<label class="relative inline-flex items-center cursor-pointer">
<input id="tab-admin-deprecation-toggle" type="checkbox" class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-red-300 dark:peer-focus:ring-red-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-red-600"></div>
<span class="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300">Is deprecated</span>
</label>
</div>
</div>
</div>
</section>
<div id="modal-add-owner" tabindex="-1" class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 w-full md:inset-0 h-modal md:h-full" style="display: none;">
<div class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-51 w-full md:inset-0 h-modal md:h-full" style="background-color: black; opacity: 0.75;"></div>
<div class="relative" style="margin: auto; margin-top: 10%; width: 800px;">
<div class="relative p-4 bg-white rounded-lg shadow dark:bg-gray-800 md:p-8">
<div class="mb-4 text-sm font-light text-gray-500 dark:text-gray-400">
<h3 class="mb-3 text-2xl font-bold text-gray-900 dark:text-white">Add new owner</h3>
</div>
<form class="mb-3 space-y-8">
<div>
<label for="add-owner-email" class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300">User email</label>
<input type="text" id="add-owner-email" class="block p-3 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 shadow-sm focus:ring-primary-500 focus:border-primary-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500 dark:shadow-sm-light">
</div>
</form>
<div class="justify-between items-center pt-0 space-y-4 sm:flex sm:space-y-0">
<div class="items-center space-y-4 sm:space-x-4 sm:flex sm:space-y-0">
<button id="modal-add-owner-close" type="button" class="py-2 px-4 w-full text-sm font-medium text-gray-500 bg-white rounded-lg border border-gray-200 sm:w-auto hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-primary-300 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">Cancel</button>
<button id="modal-add-owner-confirm" type="button" class="focus:outline-none text-white bg-green-700 hover:bg-green-800 focus:ring-4 focus:ring-green-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-green-600 dark:hover:bg-green-700 dark:focus:ring-green-900">Add</button>
</div>
</div>
</div>
</div>
</div>
<div id="modal-remove-owner" tabindex="-1" class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 w-full md:inset-0 h-modal md:h-full" style="display: none;">
<div class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-51 w-full md:inset-0 h-modal md:h-full" style="background-color: black; opacity: 0.75;"></div>
<div class="relative" style="margin: auto; margin-top: 10%; width: 800px;">
<div class="relative p-4 bg-white rounded-lg shadow dark:bg-gray-800 md:p-8">
<div class="mb-4 text-sm font-light text-gray-500 dark:text-gray-400">
<h3 class="mb-3 text-2xl font-bold text-gray-900 dark:text-white">Remove this owner?</h3>
</div>
<form class="mb-3 space-y-8">
<div>
<label for="remove-owner-email" class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300">User</label>
<input type="text" id="remove-owner-email" class="block p-3 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 shadow-sm focus:ring-primary-500 focus:border-primary-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500 dark:shadow-sm-light" disabled>
</div>
</form>
<div class="justify-between items-center pt-0 space-y-4 sm:flex sm:space-y-0">
<div class="items-center space-y-4 sm:space-x-4 sm:flex sm:space-y-0">
<button id="modal-remove-owner-close" type="button" class="py-2 px-4 w-full text-sm font-medium text-gray-500 bg-white rounded-lg border border-gray-200 sm:w-auto hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-primary-300 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">Cancel</button>
<button id="modal-remove-owner-confirm" type="button" class="focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900">Remove</button>
</div>
</div>
</div>
</div>
</div>
<div id="modal-add-target" tabindex="-1" class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 w-full md:inset-0 h-modal md:h-full" style="display: none;">
<div class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-51 w-full md:inset-0 h-modal md:h-full" style="background-color: black; opacity: 0.75;"></div>
<div class="relative" style="margin: auto; margin-top: 10%; width: 800px;">
<div class="relative p-4 bg-white rounded-lg shadow dark:bg-gray-800 md:p-8">
<div class="mb-4 text-sm font-light text-gray-500 dark:text-gray-400">
<h3 class="mb-3 text-2xl font-bold text-gray-900 dark:text-white">Add new target</h3>
</div>
<form class="mb-3 space-y-8">
<div>
<label for="add-target" class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300">Target</label>
<select id="add-target" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"></select>
</div>
<div>
<label class="relative inline-flex items-center cursor-pointer">
<input id="add-target-use-native" type="checkbox" class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-red-300 dark:peer-focus:ring-red-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-red-600"></div>
<span class="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300">Require native toolchain for documentation generation</span>
</label>
</div>
</form>
<div class="justify-between items-center pt-0 space-y-4 sm:flex sm:space-y-0">
<div class="items-center space-y-4 sm:space-x-4 sm:flex sm:space-y-0">
<button id="modal-add-target-close" type="button" class="py-2 px-4 w-full text-sm font-medium text-gray-500 bg-white rounded-lg border border-gray-200 sm:w-auto hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-primary-300 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">Cancel</button>
<button id="modal-add-target-confirm" type="button" class="focus:outline-none text-white bg-green-700 hover:bg-green-800 focus:ring-4 focus:ring-green-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-green-600 dark:hover:bg-green-700 dark:focus:ring-green-900">Add</button>
</div>
</div>
</div>
</div>
</div>
<div id="modal-remove-target" tabindex="-1" class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 w-full md:inset-0 h-modal md:h-full" style="display: none;">
<div class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-51 w-full md:inset-0 h-modal md:h-full" style="background-color: black; opacity: 0.75;"></div>
<div class="relative" style="margin: auto; margin-top: 10%; width: 800px;">
<div class="relative p-4 bg-white rounded-lg shadow dark:bg-gray-800 md:p-8">
<div class="mb-4 text-sm font-light text-gray-500 dark:text-gray-400">
<h3 class="mb-3 text-2xl font-bold text-gray-900 dark:text-white">Remove this target?</h3>
</div>
<form class="mb-3 space-y-8">
<div>
<label for="remove-target" class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300">Target</label>
<input type="text" id="remove-target" class="block p-3 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 shadow-sm focus:ring-primary-500 focus:border-primary-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500 dark:shadow-sm-light" disabled>
</div>
</form>
<div class="justify-between items-center pt-0 space-y-4 sm:flex sm:space-y-0">
<div class="items-center space-y-4 sm:space-x-4 sm:flex sm:space-y-0">
<button id="modal-remove-target-close" type="button" class="py-2 px-4 w-full text-sm font-medium text-gray-500 bg-white rounded-lg border border-gray-200 sm:w-auto hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-primary-300 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">Cancel</button>
<button id="modal-remove-target-confirm" type="button" class="focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900">Remove</button>
</div>
</div>
</div>
</div>
</div>
<div id="modal-add-capability" tabindex="-1" class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 w-full md:inset-0 h-modal md:h-full" style="display: none;">
<div class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-51 w-full md:inset-0 h-modal md:h-full" style="background-color: black; opacity: 0.75;"></div>
<div class="relative" style="margin: auto; margin-top: 10%; width: 800px;">
<div class="relative p-4 bg-white rounded-lg shadow dark:bg-gray-800 md:p-8">
<div class="mb-4 text-sm font-light text-gray-500 dark:text-gray-400">
<h3 class="mb-3 text-2xl font-bold text-gray-900 dark:text-white">Add new required capability</h3>
</div>
<form class="mb-3 space-y-8">
<div>
<label for="add-capability" class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300">Capability</label>
<input id="add-capability" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"></input>
</div>
</form>
<div class="justify-between items-center pt-0 space-y-4 sm:flex sm:space-y-0">
<div class="items-center space-y-4 sm:space-x-4 sm:flex sm:space-y-0">
<button id="modal-add-capability-close" type="button" class="py-2 px-4 w-full text-sm font-medium text-gray-500 bg-white rounded-lg border border-gray-200 sm:w-auto hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-primary-300 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">Cancel</button>
<button id="modal-add-capability-confirm" type="button" class="focus:outline-none text-white bg-green-700 hover:bg-green-800 focus:ring-4 focus:ring-green-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-green-600 dark:hover:bg-green-700 dark:focus:ring-green-900">Add</button>
</div>
</div>
</div>
</div>
</div>
<div id="modal-remove-capability" tabindex="-1" class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 w-full md:inset-0 h-modal md:h-full" style="display: none;">
<div class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-51 w-full md:inset-0 h-modal md:h-full" style="background-color: black; opacity: 0.75;"></div>
<div class="relative" style="margin: auto; margin-top: 10%; width: 800px;">
<div class="relative p-4 bg-white rounded-lg shadow dark:bg-gray-800 md:p-8">
<div class="mb-4 text-sm font-light text-gray-500 dark:text-gray-400">
<h3 class="mb-3 text-2xl font-bold text-gray-900 dark:text-white">Remove this capability?</h3>
</div>
<form class="mb-3 space-y-8">
<div>
<label for="remove-capability" class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300">Capability</label>
<input type="text" id="remove-capability" class="block p-3 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 shadow-sm focus:ring-primary-500 focus:border-primary-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500 dark:shadow-sm-light" disabled>
</div>
</form>
<div class="justify-between items-center pt-0 space-y-4 sm:flex sm:space-y-0">
<div class="items-center space-y-4 sm:space-x-4 sm:flex sm:space-y-0">
<button id="modal-remove-capability-close" type="button" class="py-2 px-4 w-full text-sm font-medium text-gray-500 bg-white rounded-lg border border-gray-200 sm:w-auto hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-primary-300 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">Cancel</button>
<button id="modal-remove-capability-confirm" type="button" class="focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900">Remove</button>
</div>
</div>
</div>
</div>
</div>
</body>
<footer class="p-4 bg-white md:p-8 lg:p-10 dark:bg-gray-800">
<div class="mx-auto max-w-screen-xl text-center">
<span class="text-sm text-gray-500 sm:text-center dark:text-gray-400">Version <span id="version"></span>, Copyright © <span id="year"></span> <a href="https://cenotelie.fr/" target="_blank" class="hover:underline">Cénotélie</a>. All Rights Reserved.</span>
</div>
</footer>
<link href="/webapp/index.css" rel="stylesheet" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/vs2015.min.css">
<script src="/webapp/api.js"></script>
<script src="/webapp/index.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/moment@2.30.1/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment"></script>
<script>
function doPageLoad() {
onPageLoad().then((user) => {
const params = getQueryParameters(window.location.search);
if (window.location.hash.length > 0) {
const id = Number(window.location.hash.substring(1));
onClickTab(id);
}
Promise.all([
Promise.resolve(user),
apiGetRegistryInformation(),
apiGetCrate(params.crate),
params.version === undefined ? apiGetCrateLastReadme(params.crate) : apiGetCrateReadmeAt(params.crate, params.version),
apiGetCrateOwners(params.crate)
]).then(([user, registryInfo, crate, readme, owners]) => renderCrate(user, registryInfo, crate, params.version, readme, owners));
});
}
function renderCrate(currentUser, registryInfo, crate, version, readme, owners) {
const currentVersion = version === undefined ? crate.versions[crate.versions.length - 1] : crate.versions.find(meta => meta.index.vers === version);
if (currentVersion.depsHasOutdated || currentVersion.depsHasCVEs) {
document.getElementById("header-dependencies-warn").style.display = "inline-block";
if (currentVersion.depsHasCVEs) {
document.getElementById("header-dependencies-warn-icon").setAttribute("stroke", "red");
}
}
if (crate.isDeprecated) {
document.getElementById("meta-deprecation").style.display = null;
}
document.getElementById("meta-name").appendChild(document.createTextNode(currentVersion.index.name));
document.getElementById("meta-name-link").setAttribute("href", `/crates/${currentVersion.index.name}`);
document.getElementById("meta-version").appendChild(document.createTextNode(`v${currentVersion.index.vers}`));
document.getElementById("meta-description").appendChild(document.createTextNode(crate.metadata?.description));
document.getElementById("meta-uploaded-on").appendChild(document.createTextNode(serializeDate(currentVersion.upload)));
document.getElementById("meta-uploaded-by").appendChild(document.createTextNode(currentVersion.uploadedBy.name));
document.getElementById("meta-uploaded-by").href = `mailto:${currentVersion.uploadedBy.email}`;
document.getElementById("meta-install").appendChild(document.createTextNode(`${currentVersion.index.name} = { version = "${currentVersion.index.vers}", registry = "${registryInfo.registryName}" }`));
for (const doc of currentVersion.docs) {
if (doc.isPresent) {
const crateRustName = currentVersion.index.name.replaceAll("-", "_");
const link = document.createElement("a");
if (doc.target === registryInfo.toolchainHost) {
// default target
link.appendChild(document.createTextNode(`v${currentVersion.index.vers}`));
link.setAttribute("href", `/docs/${currentVersion.index.name}/${currentVersion.index.vers}/${crateRustName}/index.html`);
} else {
link.appendChild(document.createTextNode(`v${currentVersion.index.vers} - ${doc.target}`));
link.setAttribute("href", `/docs/${currentVersion.index.name}/${currentVersion.index.vers}/${doc.target}/${crateRustName}/index.html`);
}
const li = document.createElement("li");
li.appendChild(link);
document.getElementById("meta-docs").appendChild(li);
}
}
document.getElementById("tab-readme-dl-total").appendChild(document.createTextNode(
crate.versions.reduce((acc, v) => acc + v.downloadCount, 0).toString()
));
document.getElementById("tab-readme-versions-count").appendChild(document.createTextNode(crate.versions.length.toString()));
const tabReadmeEl = document.getElementById("tab-readme-content");
tabReadmeEl.innerHTML = marked.parse(readme);
applyStyle(tabReadmeEl);
const tabReadmePropsEl = document.getElementById("tab-readme-props");
for (const owner of owners.users) {
tabReadmePropsEl.appendChild(renderOwner(owner));
}
const tabVersions = document.getElementById("tab-versions");
for (const version of crate.versions.reverse()) {
tabVersions.appendChild(renderVersion(version));
}
const tabFeatures = document.getElementById("tab-features");
for (const featureName of Object.getOwnPropertyNames(currentVersion.index.features)) {
tabFeatures.appendChild(renderFeature(featureName, currentVersion.index.features[featureName]));
}
renderDependencies(currentVersion.index.deps, null);
renderDocs(crate);
document.getElementById("tab-admin-deprecation-toggle").checked = crate.isDeprecated;
const canAdmin = currentUser.roles.includes("admin") || owners.users.find(u => u.id === currentUser.id) !== undefined;
if (canAdmin) {
document.getElementById("header-admin").parentElement.style.display = null;
const tabAdminOwnersEl = document.getElementById("tab-admin-owners");
for (const owner of owners.users) {
tabAdminOwnersEl.appendChild(renderAdminOwnerRow(currentVersion.index.name, owner));
}
const buttonAddOwnerEl = document.getElementById("button-add-owner");
buttonAddOwnerEl.addEventListener("click", () => openAddOwner(currentVersion.index.name));
const tabAdminTargetsEl = document.getElementById("tab-admin-targets");
if (crate.targets.length === 0) {
const targetEl = document.createElement("div");
targetEl.className = "p-4 text-sm text-gray-800 rounded-lg bg-gray-50 dark:bg-gray-800 dark:text-gray-300";
targetEl.setAttribute("role", "alert");
targetEl.appendChild(document.createTextNode(`No target specified for this crate. Documentation generation and dependency analysis will use the host target: ${registryInfo.toolchainHost}`));
tabAdminTargetsEl.appendChild(targetEl);
} else {
for (const targetInfo of crate.targets) {
tabAdminTargetsEl.appendChild(renderAdminTargetRow(currentVersion.index.name, crate.targets, targetInfo));
}
}
const buttonAddTargetEl = document.getElementById("button-add-target");
buttonAddTargetEl.addEventListener("click", () => openAddTarget(currentVersion.index.name, registryInfo.toolchainTargets, crate.targets));
const tabAdminCapsEl = document.getElementById("tab-admin-capabilities");
if (crate.capabilities.length === 0) {
const targetEl = document.createElement("div");
targetEl.className = "p-4 text-sm text-gray-800 rounded-lg bg-gray-50 dark:bg-gray-800 dark:text-gray-300";
targetEl.setAttribute("role", "alert");
targetEl.appendChild(document.createTextNode(`This crate does not require any capability. Required capabilities will be used to select appropriate worker nodes that provide them when generating documentation for this crate.`));
tabAdminCapsEl.appendChild(targetEl);
} else {
for (const capability of crate.capabilities) {
tabAdminCapsEl.appendChild(renderCapabilityRow(currentVersion.index.name, crate.capabilities, capability));
}
}
const buttonAddCapabilityEl = document.getElementById("button-add-capability");
buttonAddCapabilityEl.addEventListener("click", () => openAddCapability(currentVersion.index.name, crate.capabilities));
document.getElementById("tab-admin-deprecation-toggle").onchange = () => {
apiSetCrateDeprecation(currentVersion.index.name, !crate.isDeprecated).then(() => {
crate.isDeprecated = !crate.isDeprecated;
document.getElementById("meta-deprecation").style.display = crate.isDeprecated ? null : "none";
});
}
}
hljs.highlightAll();
apiCheckCrateDeps(currentVersion.index.name, currentVersion.index.vers).then((analysis) => {
renderDependencies(currentVersion.index.deps, analysis);
});
apiGetCrateDlStats(currentVersion.index.name).then(stats => {
const chart = new Chart(document.getElementById("tab-readme-dl-chart"), {
type: "line",
data: {
labels: stats.days,
datasets: stats.versions.map(v => ({
label: v.version,
data: v.counts,
}))
},
options: {
scales: {
x: {
type: 'timeseries',
}
}
}
});
});
}
function renderAdminOwnerRow(crateName, owner) {
const ownerRendering = renderOwner(owner);
const button = document.createElement("button");
button.type = "button";
button.className = "focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-xs px-3 py-1 me-2 mb-1 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900";
button.appendChild(document.createTextNode("-"));
button.addEventListener("click", () => openRemoveOwner(crateName, owner));
const wrapper = document.createElement("div");
wrapper.appendChild(button);
wrapper.appendChild(ownerRendering);
return wrapper;
}
function renderOwner(owner) {
const wrapper = document.createElement("a");
wrapper.href = `mailto:${owner.email}`;
wrapper.className = "ml-4 font-normal text-gray-700 dark:text-gray-400";
wrapper.innerHTML += '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6" style="display: inline;">\
<path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />\
</svg>';
wrapper.appendChild(document.createTextNode(" "));
wrapper.appendChild(document.createTextNode(owner.name));
return wrapper;
}
function renderAdminTargetRow(crateName, currentTargets, targetInfo) {
const button = document.createElement("button");
button.type = "button";
button.className = "focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-xs px-3 py-1 me-2 mb-1 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900";
button.appendChild(document.createTextNode("-"));
button.addEventListener("click", () => openRemoveTarget(crateName, currentTargets, targetInfo));
const targetEl = document.createElement("span");
targetEl.className = "ml-4 font-normal text-gray-700 dark:text-gray-400";
targetEl.appendChild(document.createTextNode(targetInfo.target));
const useNativeEl = document.createElement("span");
useNativeEl.className = "ml-4 font-normal text-red-700 dark:text-red-400";
if (targetInfo.docsUseNative) {
useNativeEl.appendChild(document.createTextNode(" requires native toolchain"));
}
const wrapper = document.createElement("div");
wrapper.appendChild(button);
wrapper.appendChild(targetEl);
wrapper.appendChild(useNativeEl);
return wrapper;
}
function renderCapabilityRow(crateName, currentCapabilities, capability) {
const button = document.createElement("button");
button.type = "button";
button.className = "focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-xs px-3 py-1 me-2 mb-1 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900";
button.appendChild(document.createTextNode("-"));
button.addEventListener("click", () => openRemoveCapability(crateName, currentCapabilities, capability));
const capabilityEl = document.createElement("span");
capabilityEl.className = "ml-4 font-normal text-gray-700 dark:text-gray-400";
capabilityEl.appendChild(document.createTextNode(capability));
const wrapper = document.createElement("div");
wrapper.appendChild(button);
wrapper.appendChild(capabilityEl);
return wrapper;
}
function renderVersion(version) {
const card = document.createElement("a");
card.href = `/crates/${version.index.name}/${version.index.vers}`;
card.className = "flex block mb-4 p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700";
const wrapper = document.createElement("div");
wrapper.className = "flex items-center me-4";
const title = document.createElement("h5");
title.className = "text-xl font-bold tracking-tight text-gray-900 dark:text-white";
title.appendChild(document.createTextNode(version.index.vers));
wrapper.appendChild(title);
wrapper.innerHTML += '<p class="ml-4 font-normal text-gray-700 dark:text-gray-400">\
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">\
<path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />\
</svg>\
</p>';
const dataUploadedBy = document.createElement("p");
dataUploadedBy.className = "font-normal text-gray-700 dark:text-gray-400";
dataUploadedBy.appendChild(document.createTextNode(`Uploaded by: ${version.uploadedBy.name}`));
wrapper.appendChild(dataUploadedBy);
wrapper.innerHTML += '<p class="ml-4 font-normal text-gray-700 dark:text-gray-400">\
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">\
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5m-9-6h.008v.008H12v-.008ZM12 15h.008v.008H12V15Zm0 2.25h.008v.008H12v-.008ZM9.75 15h.008v.008H9.75V15Zm0 2.25h.008v.008H9.75v-.008ZM7.5 15h.008v.008H7.5V15Zm0 2.25h.008v.008H7.5v-.008Zm6.75-4.5h.008v.008h-.008v-.008Zm0 2.25h.008v.008h-.008V15Zm0 2.25h.008v.008h-.008v-.008Zm2.25-4.5h.008v.008H16.5v-.008Zm0 2.25h.008v.008H16.5V15Z" />\
</svg>\
</p>';
const dataUploadedOn = document.createElement("p");
dataUploadedOn.className = "font-normal text-gray-700 dark:text-gray-400";
dataUploadedOn.appendChild(document.createTextNode(`Uploaded on: ${serializeDate(version.upload)}`));
wrapper.appendChild(dataUploadedOn);
wrapper.innerHTML += '<p class="ml-4 font-normal text-gray-700 dark:text-gray-400">\
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">\
<path stroke-linecap="round" stroke-linejoin="round" d="M12 10.5v6m3-3H9m4.06-7.19-2.12-2.12a1.5 1.5 0 0 0-1.061-.44H4.5A2.25 2.25 0 0 0 2.25 6v12a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9a2.25 2.25 0 0 0-2.25-2.25h-5.379a1.5 1.5 0 0 1-1.06-.44Z" />\
</svg>\
</p>';
const dataFeatures = document.createElement("p");
dataFeatures.className = "font-normal text-gray-700 dark:text-gray-400";
dataFeatures.appendChild(document.createTextNode(`${Object.getOwnPropertyNames(version.index.features).length} features`));
wrapper.appendChild(dataFeatures);
wrapper.innerHTML += '<p class="ml-4 font-normal text-gray-700 dark:text-gray-400">\
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">\
<path stroke-linecap="round" stroke-linejoin="round" d="M7.217 10.907a2.25 2.25 0 1 0 0 2.186m0-2.186c.18.324.283.696.283 1.093s-.103.77-.283 1.093m0-2.186 9.566-5.314m-9.566 7.5 9.566 5.314m0 0a2.25 2.25 0 1 0 3.935 2.186 2.25 2.25 0 0 0-3.935-2.186Zm0-12.814a2.25 2.25 0 1 0 3.933-2.185 2.25 2.25 0 0 0-3.933 2.185Z" />\
</svg>\
</p>';
const dataDeps = document.createElement("p");
dataDeps.className = "font-normal text-gray-700 dark:text-gray-400";
dataDeps.appendChild(document.createTextNode(`${version.index.deps.length} dependencies`));
wrapper.appendChild(dataDeps);
card.appendChild(wrapper);
return card;
}
function renderFeature(featureName, feature) {
const card = document.createElement("div");
card.className = "flex block mb-4 p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700";
const wrapper = document.createElement("div");
wrapper.className = "flex items-center me-4";
const title = document.createElement("h5");
title.className = "text-xl font-bold tracking-tight text-gray-900 dark:text-white";
title.appendChild(document.createTextNode(featureName));
wrapper.appendChild(title);
const content = document.createElement("p");
content.className = "ml-4 font-normal text-gray-700 dark:text-gray-400";
content.appendChild(document.createTextNode(feature.join(", ")));
wrapper.appendChild(content);
card.appendChild(wrapper);
return card;
}
function renderDependencies(deps, analysis) {
const tabDependencies = document.getElementById("tab-dependencies");
while (tabDependencies.lastElementChild !== null) {
tabDependencies.removeChild(tabDependencies.lastElementChild);
}
const normalDeps = [];
const devDeps = [];
const buildDeps = [];
let index = 0;
for (const dep of deps) {
const target = dep.kind === "normal" ? normalDeps : dep.kind === "dev" ? devDeps : buildDeps;
if (analysis === null) {
target.push([dep, null]);
} else {
target.push([dep, analysis.directDependencies[index]]);
}
index += 1;
}
if (normalDeps.length > 0) {
renderDependenciesCategory(tabDependencies, "Dependencies", normalDeps);
}
if (devDeps.length > 0) {
renderDependenciesCategory(tabDependencies, "Dev dependencies", devDeps);
}
if (buildDeps.length > 0) {
renderDependenciesCategory(tabDependencies, "Build dependencies", buildDeps);
}
// reapply tab head rendering
if (analysis !== null) {
const depsHasOutdated = analysis.directDependencies.reduce((acc, dep) => acc || dep.isOutdated, false);
const depsHasCVEs = analysis.advisories.length > 0;
if (depsHasOutdated || depsHasCVEs) {
document.getElementById("header-dependencies-warn").style.display = "inline-block";
if (depsHasCVEs) {
document.getElementById("header-dependencies-warn-icon").setAttribute("stroke", "red");
}
}
}
if (analysis !== null && analysis.advisories.length > 0) {
const title = document.createElement("h5");
title.className = "text-xl font-bold tracking-tight text-gray-900 dark:text-white my-10";
title.appendChild(document.createTextNode("Security Vulnerabilities"));
tabDependencies.appendChild(title);
for (const advisory of analysis.advisories) {
tabDependencies.appendChild(renderAdvisory(advisory));
}
}
}
function renderDependenciesCategory(tabDependencies, name, depsWithInfo) {
const title = document.createElement("h5");
title.className = "text-xl font-bold tracking-tight text-gray-900 dark:text-white my-10";
title.appendChild(document.createTextNode(name));
tabDependencies.appendChild(title);
const tableWrapper = document.createElement("div");
tableWrapper.className = "relative overflow-x-auto shadow-md sm:rounded-lg";
tabDependencies.appendChild(tableWrapper);
const table = document.createElement("table");
table.className = "w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400";
tableWrapper.appendChild(table);
table.innerHTML += '<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">\
<tr>\
<th scope="col" class="px-6 py-3">\
Crate\
</th>\
<th scope="col" class="px-6 py-3">\
Features\
</th>\
<th scope="col" class="px-6 py-3">\
Required\
</th>\
<th scope="col" class="px-6 py-3">\
Latest\
</th>\
<th scope="col" class="px-6 py-3">\
Status\
</th>\
</tr>\
</thead>';
const tBody = document.createElement("tbody");
table.appendChild(tBody);
for (const [dep, depInfo] of depsWithInfo) {
tBody.appendChild(renderDependency(dep, depInfo));
}
}
function renderDependency(dep, depInfo) {
const row = document.createElement("tr");
row.className = "odd:bg-white odd:dark:bg-gray-900 even:bg-gray-50 even:dark:bg-gray-800 border-b dark:border-gray-700";
const cellHead = document.createElement("th");
cellHead.setAttribute("scope", "row");
cellHead.className = "px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white";
const link = document.createElement("a");
if (dep.registry === null) {
link.href = `/crates/${dep.name}`;
} else if (dep.registry === "https://github.com/rust-lang/crates.io-index") {
link.target = "_blank";
link.href = `https://crates.io/crates/${dep.name}`;
} else {
let reg = dep.registry;
if (!reg.endsWith("/")) {
reg += "/";
}
link.target = "_blank";
link.href = `${reg}crates/${dep.name}`;
}
link.appendChild(document.createTextNode(dep.name));
cellHead.appendChild(link);
if (dep.optional) {
cellHead.appendChild(document.createTextNode(", optional"));
}
row.appendChild(cellHead);
const cellFeatures = document.createElement("td");
cellFeatures.className = "px-6 py-4";
if (!dep.default_features) {
const marker = document.createElement("span");
marker.className = "font-bold";
marker.appendChild(document.createTextNode("no default feature"));
cellFeatures.appendChild(marker);
}
cellFeatures.appendChild(document.createTextNode(" "));
if (dep.features.length > 0) {
cellFeatures.appendChild(document.createTextNode(dep.features.join(",")));
}
row.appendChild(cellFeatures);
const cellReq = document.createElement("td");
cellReq.className = "px-6 py-4";
cellReq.appendChild(document.createTextNode(dep.req));
row.appendChild(cellReq);
const cellLatest = document.createElement("td");
cellLatest.className = "px-6 py-4";
if (depInfo !== null) {
cellLatest.appendChild(document.createTextNode(depInfo.lastVersion));
}
row.appendChild(cellLatest);
const cellStatus = document.createElement("td");
cellStatus.className = "px-6 py-4";
if (depInfo !== null) {
const color = depInfo.isOutdated ? "yellow" : "green";
const span = document.createElement("span");
span.className = `bg-${color}-100 text-${color}-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-${color}-900 dark:text-${color}-300`;
span.appendChild(document.createTextNode(depInfo.isOutdated ? "out of date" : "up to date"));
cellStatus.appendChild(span);
}
row.appendChild(cellStatus);
return row;
}
function renderAdvisory(advisory) {
const color = "red";
const card = document.createElement("a");
card.className = `block m-2 p-2 bg-white border border-${color}-200 rounded-lg shadow hover:bg-${color}-100 dark:bg-${color}-800 dark:border-${color}-700 dark:hover:bg-${color}-700`;
card.target = "_blank";
card.href = `https://rustsec.org/advisories/${advisory.content.id}.html`;
const title = document.createElement("h5");
title.className = `mb-1 text-xl font-bold tracking-tight text-${color}-900 dark:text-${color}-100`;
title.appendChild(document.createTextNode(`${advisory.content.id}: ${advisory.package} - ${advisory.version}`));
card.appendChild(title);
const sub = document.createElement("p");
sub.className = `font-normal text-${color}-700 dark:text-${color}-400`;
sub.appendChild(document.createTextNode(advisory.content.summary));
card.appendChild(sub);
return card;
}
function renderDocs(crate) {
const tableEl = document.getElementById("tab-docs-table");
for (const version of crate.versions) {
tableEl.appendChild(renderDocsVersion(version));
}
}
function renderDocsVersion(version) {
const hasMissing = version.docs.length === 0 || version.docs.reduce((acc, doc) => acc || !doc.isPresent, false);
const versionEl = document.createElement("span");
versionEl.className = "font-medium text-blue-600 dark:text-blue-500";
versionEl.appendChild(document.createTextNode(version.index.vers));
const row = document.createElement("tr");
const cell1 = document.createElement("th");
cell1.setAttribute("scope", "row");
cell1.className = "px-6 py-4 font-medium";
cell1.appendChild(versionEl);
const cell2 = document.createElement("td");
cell2.className = "px-6 py-4";
const cell3 = document.createElement("td");
cell3.className = "px-6 py-4";
for (const doc of version.docs) {
const color = doc.isPresent ? "green" : "red";
const statusEl = document.createElement("span");
statusEl.className = `mx-3 bg-${color}-100 text-${color}-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-${color}-900 dark:text-${color}-300`;
statusEl.appendChild(document.createTextNode(doc.isPresent ? "available" : "missing"));
const crateRustName = version.index.name.replaceAll("-", "_");
const linkEl = document.createElement("a");
linkEl.appendChild(document.createTextNode(doc.target));
linkEl.setAttribute("href", `/docs/${version.index.name}/${version.index.vers}/${doc.target}/${crateRustName}/index.html`);
const wrapper = document.createElement("div");
wrapper.appendChild(linkEl);
wrapper.appendChild(statusEl);
wrapper.style.display = "inline-block";
cell2.appendChild(wrapper);
}
if (hasMissing) {
// <button type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Default</button>
const refreshEl = document.createElement("button");
refreshEl.type = "button";
refreshEl.className = "text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800";
refreshEl.appendChild(document.createTextNode("generate"));
refreshEl.onclick = () => {
apiRegenCrateDoc(version.index.name, version.index.vers).then(() => {
refreshEl.remove();
});
};
cell3.appendChild(refreshEl);
}
row.appendChild(cell1);
row.appendChild(cell2);
row.appendChild(cell3);
return row;
}
function onClickTab(index) {
const headers = [
document.getElementById("header-readme"),
document.getElementById("header-versions"),
document.getElementById("header-features"),
document.getElementById("header-dependencies"),
document.getElementById("header-docs"),
document.getElementById("header-admin"),
];
const tabs = [
document.getElementById("tab-readme"),
document.getElementById("tab-versions"),
document.getElementById("tab-features"),
document.getElementById("tab-dependencies"),
document.getElementById("tab-docs"),
document.getElementById("tab-admin"),
];
for (const header of headers) {
header.className = "inline-block p-4 rounded-t-lg hover:text-gray-600 hover:bg-gray-50 dark:hover:bg-gray-800 dark:hover:text-gray-300";
}
for (const tab of tabs) {
tab.style.display = "none";
}
headers[index].className = "inline-block p-4 text-blue-600 bg-gray-100 rounded-t-lg active dark:bg-gray-800 dark:text-blue-500";
tabs[index].style.display = null;
window.location.hash = `#${index}`;
}
function applyStyle(node) {
if (node.tagName === "H1") {
node.className = "mb-2 text-4xl font-bold tracking-tight text-gray-900 dark:text-white";
}
if (node.tagName === "H2") {
node.className = "mb-2 text-3xl font-bold tracking-tight text-gray-900 dark:text-white";
}
if (node.tagName === "H3") {
node.className = "mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white";
}
if (node.tagName === "H4") {
node.className = "mb-2 text-xl font-bold tracking-tight text-gray-900 dark:text-white";
}
if (node.tagName === "P") {
node.className = "mb-3 font-normal text-gray-700 dark:text-gray-400";
}
if (node.tagName === "UL") {
node.className = "max-w-md space-y-1 text-gray-500 list-disc list-inside dark:text-gray-400";
}
for (let i = 0; i != node.children.length; i++) {
applyStyle(node.children.item(i));
}
}
function openAddOwner(crateName) {
const modalEl = document.getElementById('modal-add-owner');
modalEl.style.display = "unset";
const userEmailEl = document.getElementById("add-owner-email");
const closeEl = document.getElementById('modal-add-owner-close');
closeEl.addEventListener('click', function() {
modalEl.style.display = "none";
});
const confirmEl = document.getElementById('modal-add-owner-confirm');
confirmEl.addEventListener('click', function() {
closeEl.disabled = true;
confirmEl.disabled = true;
apiAddCrateOwner(crateName, userEmailEl.value).then((_) => {
window.location.reload();
}).finally(() => {
closeEl.disabled = false;
confirmEl.disabled = false;
});
});
}
function openRemoveOwner(crateName, user) {
const modalEl = document.getElementById('modal-remove-owner');
modalEl.style.display = "unset";
const userEmailEl = document.getElementById("remove-owner-email");
userEmailEl.value = user.email;
const closeEl = document.getElementById('modal-remove-owner-close');
closeEl.addEventListener('click', function() {
modalEl.style.display = "none";
});
const confirmEl = document.getElementById('modal-remove-owner-confirm');
confirmEl.addEventListener('click', function() {
closeEl.disabled = true;
confirmEl.disabled = true;
apiRemoveCrateOwners(crateName, user.email).then((_) => {
window.location.reload();
}).finally(() => {
closeEl.disabled = false;
confirmEl.disabled = false;
});
});
}
function openAddTarget(crateName, toolchainTargets, currentTargets) {
const modalEl = document.getElementById('modal-add-target');
modalEl.style.display = "unset";
const targetEl = document.getElementById("add-target");
removeAllChildren(targetEl);
for (const target of toolchainTargets.filter(t => !currentTargets.includes(t))) {
const optionEl = document.createElement("option");
optionEl.value = target;
optionEl.appendChild(document.createTextNode(target));
targetEl.appendChild(optionEl);
}
const useNativeEl = document.getElementById("add-target-use-native");
useNativeEl.checked = false;
const closeEl = document.getElementById('modal-add-target-close');
closeEl.addEventListener('click', function() {
modalEl.style.display = "none";
});
const confirmEl = document.getElementById('modal-add-target-confirm');
confirmEl.addEventListener('click', function() {
closeEl.disabled = true;
confirmEl.disabled = true;
apiSetCrateTargets(crateName, [...currentTargets, {target: targetEl.value, docsUseNative: useNativeEl.checked}]).then((_) => {
window.location.reload();
}).finally(() => {
closeEl.disabled = false;
confirmEl.disabled = false;
});
});
}
function openRemoveTarget(crateName, currentTargets, targetInfo) {
const modalEl = document.getElementById('modal-remove-target');
modalEl.style.display = "unset";
const targetEl = document.getElementById("remove-target");
targetEl.value = targetInfo.target;
const closeEl = document.getElementById('modal-remove-target-close');
closeEl.addEventListener('click', function() {
modalEl.style.display = "none";
});
const confirmEl = document.getElementById('modal-remove-target-confirm');
confirmEl.addEventListener('click', function() {
closeEl.disabled = true;
confirmEl.disabled = true;
const newTargets = currentTargets.filter(t => t !== targetInfo);
apiSetCrateTargets(crateName, newTargets).then((_) => {
window.location.reload();
}).finally(() => {
closeEl.disabled = false;
confirmEl.disabled = false;
});
});
}
function openAddCapability(crateName, currentCapabilities) {
const modalEl = document.getElementById('modal-add-capability');
modalEl.style.display = "unset";
const capabilityEl = document.getElementById("add-capability");
capabilityEl.value = "";
const closeEl = document.getElementById('modal-add-capability-close');
closeEl.addEventListener('click', function() {
modalEl.style.display = "none";
});
const confirmEl = document.getElementById('modal-add-capability-confirm');
confirmEl.addEventListener('click', function() {
closeEl.disabled = true;
confirmEl.disabled = true;
apiSetCrateCapabilities(crateName, [...currentCapabilities, capabilityEl.value]).then((_) => {
window.location.reload();
}).finally(() => {
closeEl.disabled = false;
confirmEl.disabled = false;
});
});
}
function openRemoveCapability(crateName, currentCapabilities, capability) {
const modalEl = document.getElementById('modal-remove-capability');
modalEl.style.display = "unset";
const capabilityEl = document.getElementById("remove-capability");
capabilityEl.value = capability;
const closeEl = document.getElementById('modal-remove-capability-close');
closeEl.addEventListener('click', function() {
modalEl.style.display = "none";
});
const confirmEl = document.getElementById('modal-remove-capability-confirm');
confirmEl.addEventListener('click', function() {
closeEl.disabled = true;
confirmEl.disabled = true;
const newCapabilities = currentCapabilities.filter(t => t !== capability);
apiSetCrateCapabilities(crateName, newCapabilities).then((_) => {
window.location.reload();
}).finally(() => {
closeEl.disabled = false;
confirmEl.disabled = false;
});
});
}
</script>
</html>