Read more data from publish metadata
Diff
chartered-db/src/crates.rs | 41 +++++++++++++++++++++++++++++++++++------
chartered-db/src/schema.rs | 5 +++++
chartered-frontend/src/index.sass | 1 +
chartered-git/src/main.rs | 2 +-
chartered-types/src/cargo.rs | 9 +++++++++
migrations/2021-08-31-214501_create_crates_table/up.sql | 5 +++++
chartered-frontend/src/pages/SingleCrate.tsx | 288 ++++++++++++++++++++++++++++++++++++++++++--------------------------------------
chartered-web/src/endpoints/cargo_api/publish.rs | 13 +++----------
chartered-web/src/endpoints/web_api/crate_info.rs | 17 ++++++++++++++---
9 files changed, 174 insertions(+), 207 deletions(-)
@@ -21,6 +21,14 @@
pub version: String,
pub filesystem_object: String,
pub yanked: bool,
pub readme: Option<String>,
pub description: Option<String>,
pub repository: Option<String>,
pub homepage: Option<String>,
pub documentation: Option<String>,
pub checksum: String,
pub dependencies: CrateDependencies<'a>,
pub features: CrateFeatures,
@@ -29,14 +37,23 @@
impl<'a> CrateVersion<'a> {
#[must_use]
pub fn into_cargo_format(self, crate_: &'a Crate) -> chartered_types::cargo::CrateVersion<'a> {
chartered_types::cargo::CrateVersion {
name: crate_.name.as_str().into(),
vers: self.version.into(),
deps: self.dependencies.0,
features: self.features.0,
links: self.links.map(Into::into),
}
pub fn into_cargo_format(self, crate_: &'a Crate) -> (chartered_types::cargo::CrateVersion<'a>, chartered_types::cargo::CrateVersionMetadata) {
(
chartered_types::cargo::CrateVersion {
name: crate_.name.as_str().into(),
vers: self.version.into(),
deps: self.dependencies.0,
features: self.features.0,
links: self.links.map(Into::into),
},
chartered_types::cargo::CrateVersionMetadata {
description: self.description,
readme: self.readme,
repository: self.repository,
homepage: self.homepage,
documentation: self.documentation,
}
)
}
}
@@ -163,10 +180,11 @@
file_identifier: chartered_fs::FileReference,
file_checksum: String,
given: chartered_types::cargo::CrateVersion<'static>,
metadata: chartered_types::cargo::CrateVersionMetadata,
) -> Result<()> {
use crate::schema::crate_versions::dsl::{
checksum, crate_id, crate_versions, dependencies, features, filesystem_object, links,
version,
version, description, readme, repository, homepage, documentation,
};
tokio::task::spawn_blocking(move || {
@@ -181,6 +199,11 @@
dependencies.eq(CrateDependencies(given.deps)),
features.eq(CrateFeatures(given.features)),
links.eq(given.links),
description.eq(metadata.description),
readme.eq(metadata.readme),
repository.eq(metadata.repository),
homepage.eq(metadata.homepage),
documentation.eq(metadata.documentation),
))
.execute(&conn)?;
@@ -5,6 +5,11 @@
version -> Text,
filesystem_object -> Text,
yanked -> Bool,
readme -> Nullable<Text>,
description -> Nullable<Text>,
repository -> Nullable<Text>,
homepage -> Nullable<Text>,
documentation -> Nullable<Text>,
checksum -> Text,
dependencies -> Binary,
features -> Binary,
@@ -1,5 +1,6 @@
$primary: #0d6efd
$font-family-monospace: "Source Code Pro", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace
$enable-cssgrid: true
@import "~bootstrap/scss/bootstrap.scss"
@import "../node_modules/source-code-pro/source-code-pro.css"
@@ -365,7 +365,7 @@
for version in versions {
let cksum = version.checksum.clone();
let yanked = version.yanked;
let version = version.into_cargo_format(&crate_def);
let (version, _) = version.into_cargo_format(&crate_def);
let entry = CrateFileEntry {
inner: &version,
@@ -29,6 +29,15 @@
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct CrateVersionMetadata {
pub description: Option<String>,
pub readme: Option<String>,
pub repository: Option<String>,
pub homepage: Option<String>,
pub documentation: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct CrateDependency<'a> {
pub name: Cow<'a, str>,
pub version_req: Cow<'a, str>,
@@ -9,6 +9,11 @@
version VARCHAR(255) NOT NULL,
filesystem_object VARCHAR(255) NOT NULL,
yanked BOOLEAN NOT NULL DEFAULT FALSE,
readme TEXT,
description VARCHAR(255),
repository VARCHAR(255),
homepage VARCHAR(255),
documentation VARCHAR(255),
checksum VARCHAR(255) NOT NULL,
dependencies BLOB NOT NULL,
features BLOB NOT NULL,
@@ -5,7 +5,7 @@
import { Link } from "react-router-dom";
import { useAuth } from '../useAuth';
import Nav from "../sections/Nav";
import { Box } from 'react-bootstrap-icons';
import { Box, HouseDoor, Book, Building, PersonPlus } from 'react-bootstrap-icons';
import { useParams } from "react-router-dom";
import { authenticatedEndpoint } from '../util';
@@ -13,11 +13,14 @@
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
type Tab = 'readme' | 'versions' | 'members';
export default function SingleCrate() {
const auth = useAuth();
const { crate } = useParams();
const [crateInfo, setCrateInfo] = useState(null);
const [currentTab, setCurrentTab] = useState<Tab>('readme');
useEffect(async () => {
let res = await fetch(authenticatedEndpoint(auth, `crates/${crate}`));
@@ -30,167 +33,7 @@
}
const crateVersion = crateInfo.versions[crateInfo.versions.length - 1];
const readme = `
# Rand
[![Test Status](https:
[![Crate](https:
[![Book](https:
[![API](https:
[![API](https:
[![Minimum rustc version](https:
A Rust library for random number generation, featuring:
- Easy random value generation and usage via the [\`Rng\`](https:
[\`SliceRandom\`](https:
[\`IteratorRandom\`](https:
- Secure seeding via the [\`getrandom\` crate](https:
and fast, convenient generation via [\`thread_rng\`](https:
- A modular design built over [\`rand_core\`](https:
([see the book](https:
- Fast implementations of the best-in-class [cryptographic](https:
[non-cryptographic](https:
- A flexible [\`distributions\`](https:
- Samplers for a large number of random number distributions via our own
[\`rand_distr\`](https:
the [\`statrs\`](https:
- [Portably reproducible output](https:
- \`#[no_std]\` compatibility (partial)
- *Many* performance optimisations
It's also worth pointing out what \`rand\` *is not*:
- Small. Most low-level crates are small, but the higher-level \`rand\` and
\`rand_distr\` each contain a lot of functionality.
- Simple (implementation). We have a strong focus on correctness, speed and flexibility, but
not simplicity. If you prefer a small-and-simple library, there are
alternatives including [fastrand](https:
and [oorandom](https:
- Slow. We take performance seriously, with considerations also for set-up
time of new distributions, commonly-used parameters, and parameters of the
current sampler.
Documentation:
- [The Rust Rand Book](https:
- [API reference (master branch)](https:
- [API reference (docs.rs)](https:
## Usage
Add this to your \`Cargo.toml\`:
\`\`\`toml
[dependencies]
rand = "0.8.0"
\`\`\`
To get started using Rand, see [The Book](https:
## Versions
Rand is *mature* (suitable for general usage, with infrequent breaking releases
which minimise breakage) but not yet at 1.0. We maintain compatibility with
pinned versions of the Rust compiler (see below).
Current Rand versions are:
- Version 0.7 was released in June 2019, moving most non-uniform distributions
to an external crate, moving \`from_entropy\` to \`SeedableRng\`, and many small
changes and fixes.
- Version 0.8 was released in December 2020 with many small changes.
A detailed [changelog](CHANGELOG.md) is available for releases.
When upgrading to the next minor series (especially 0.4 → 0.5), we recommend
reading the [Upgrade Guide](https:
Rand has not yet reached 1.0 implying some breaking changes may arrive in the
future ([SemVer](https:
breaking changes), but is considered *mature*: breaking changes are minimised
and breaking releases are infrequent.
Rand libs have inter-dependencies and make use of the
[semver trick](https:
compatible across crate versions. (This is especially important for \`RngCore\`
and \`SeedableRng\`.) A few crate releases are thus compatibility shims,
depending on the *next* lib version (e.g. \`rand_core\` versions \`0.2.2\` and
\`0.3.1\`). This means, for example, that \`rand_core_0_4_0::SeedableRng\` and
\`rand_core_0_3_0::SeedableRng\` are distinct, incompatible traits, which can
cause build errors. Usually, running \`cargo update\` is enough to fix any issues.
### Yanked versions
Some versions of Rand crates have been yanked ("unreleased"). Where this occurs,
the crate's CHANGELOG *should* be updated with a rationale, and a search on the
issue tracker with the keyword \`yank\` *should* uncover the motivation.
### Rust version requirements
Since version 0.8, Rand requires **Rustc version 1.36 or greater**.
Rand 0.7 requires Rustc 1.32 or greater while versions 0.5 require Rustc 1.22 or
greater, and 0.4 and 0.3 (since approx. June 2017) require Rustc version 1.15 or
greater. Subsets of the Rand code may work with older Rust versions, but this is
not supported.
Continuous Integration (CI) will always test the minimum supported Rustc version
(the MSRV). The current policy is that this can be updated in any
Rand release if required, but the change must be noted in the changelog.
## Crate Features
Rand is built with these features enabled by default:
- \`std\` enables functionality dependent on the \`std\` lib
- \`alloc\` (implied by \`std\`) enables functionality requiring an allocator
- \`getrandom\` (implied by \`std\`) is an optional dependency providing the code
behind \`rngs::OsRng\`
- \`std_rng\` enables inclusion of \`StdRng\`, \`thread_rng\` and \`random\`
(the latter two *also* require that \`std\` be enabled)
Optionally, the following dependencies can be enabled:
- \`log\` enables logging via the \`log\` crate\` crate
Additionally, these features configure Rand:
- \`small_rng\` enables inclusion of the \`SmallRng\` PRNG
- \`nightly\` enables some optimizations requiring nightly Rust
- \`simd_support\` (experimental) enables sampling of SIMD values
(uniformly random SIMD integers and floats), requiring nightly Rust
- \`min_const_gen\` enables generating random arrays of
any size using min-const-generics, requiring Rust ≥ 1.51.
Note that nightly features are not stable and therefore not all library and
compiler versions will be compatible. This is especially true of Rand's
experimental \`simd_support\` feature.
Rand supports limited functionality in \`no_std\` mode (enabled via
\`default-features = false\`). In this case, \`OsRng\` and \`from_entropy\` are
unavailable (unless \`getrandom\` is enabled), large parts of \`seq\` are
unavailable (unless \`alloc\` is enabled), and \`thread_rng\` and \`random\` are
unavailable.
### WASM support
The WASM target \`wasm32-unknown-unknown\` is not *automatically* supported by
\`rand\` or \`getrandom\`. To solve this, either use a different target such as
\`wasm32-wasi\` or add a direct dependency on \`getrandom\` with the \`js\` feature
(if the target supports JavaScript). See
[getrandom#WebAssembly support](https:
# License
Rand is distributed under the terms of both the MIT license and the
Apache License (Version 2.0).
See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT), and
[COPYRIGHT](COPYRIGHT) for details.`;
return (
<div className="text-white">
<Nav />
@@ -209,7 +52,7 @@
<h2 className="text-secondary m-0">{crateVersion.vers}</h2>
</div>
<p className="m-0">Random number generators and other randomness functionality.</p>
<p className="m-0">{crateVersion.description}</p>
</div>
</div>
</div>
@@ -217,7 +60,9 @@
<div className="col-md-6">
<div className="card border-0 shadow-sm text-black h-100">
<div className="card-body">
test
<HouseDoor /> <a href={crateVersion.homepage}>{crateVersion.homepage}</a><br />
<Book /> <a href={crateVersion.documentation}>{crateVersion.documentation}</a><br />
<Building /> <a href={crateVersion.repository}>{crateVersion.repository}</a>
</div>
</div>
</div>
@@ -229,35 +74,31 @@
<div className="card-header">
<ul className="nav nav-pills card-header-pills">
<li className="nav-item">
<a className="nav-link active bg-primary bg-gradient" href="#">Readme</a>
<a className={`nav-link ${currentTab == 'readme' ? 'bg-primary bg-gradient active' : ''}`} href="#"
onClick={() => setCurrentTab('readme')}>
Readme
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">
<a className={`nav-link ${currentTab == 'versions' ? 'bg-primary bg-gradient active' : ''}`} href="#"
onClick={() => setCurrentTab('versions')}>
Versions
<span className="badge rounded-pill bg-danger ms-1">{crateInfo.versions.length}</span>
<span className={`badge rounded-pill bg-danger ms-1`}>{crateInfo.versions.length}</span>
</a>
</li>
<li className="nav-item">
<a className={`nav-link ${currentTab == 'members' ? 'bg-primary bg-gradient active' : ''}`} href="#"
onClick={() => setCurrentTab('members')}>
Members
</a>
</li>
</ul>
</div>
<div className="card-body">
<ReactMarkdown children={readme} remarkPlugins={[remarkGfm]} components={{
code({node, inline, className, children, ...props}) {
const match = /language-(\w+)/.exec(className || '')
return !inline && match ? (
<Prism
children={String(children).replace(/\n$/, '')}
language={match[1]}
PreTag="div"
{...props}
/>
) : (
<code className={className} {...props}>
{children}
</code>
)
}
}} />
{currentTab == 'readme' ? <ReadMe crateInfo={crateVersion} /> : <></>}
{currentTab == 'versions' ? <>Versions</> : <></>}
{currentTab == 'members' ? <Members crateInfo={crateVersion} /> : <></>}
</div>
</div>
</div>
@@ -270,7 +111,7 @@
<ul className="list-group list-group-flush mb-2">
{crateVersion.deps.map(dep => (
<li key={dep} className="list-group-item">{dep.name} = "<strong>{dep.version_req}</strong>"</li>
<li key={`${dep.name}-${dep.version_req}`} className="list-group-item">{dep.name} = "<strong>{dep.version_req}</strong>"</li>
))}
</ul>
</div>
@@ -290,5 +131,84 @@
</div>
</div>
</div>
);
}
function ReadMe(props: { crateInfo: any }) {
return (
<ReactMarkdown children={props.crateInfo.readme} remarkPlugins={[remarkGfm]} components={{
code({node, inline, className, children, ...props}) {
const match = /language-(\w+)/.exec(className || '')
return !inline && match ? (
<Prism
children={String(children).replace(/\n$/, '')}
language={match[1]}
PreTag="pre"
{...props}
/>
) : (
<code className={className} {...props}>
{children}
</code>
)
}
}} />
);
}
function Members(props: { crateInfo: any }) {
return <div className="grid" style={{ gridTemplateColumns: '1fr 1fr 1fr' }}>
<div className="g-col-4 g-col-lg-1 d-flex align-items-center">
<img src="http://placekitten.com/96/96" className="rounded-circle" />
<div className="ms-2">
<strong>Johnny Davidson</strong> <em>(that's you!)</em><br />
Owner
</div>
</div>
<div className="g-col-4 g-col-lg-1 d-flex align-items-center mt-2">
<img src="http://placekitten.com/96/96" className="rounded-circle" />
<div className="ms-2">
<strong>Will Woodwood</strong><br />
<select className="form-select form-select-sm" aria-label="Default select example">
<option value="1">Consumer</option>
<option value="2">Maintainer</option>
<option value="3">Owner</option>
</select>
</div>
</div>
<div className="g-col-4 g-col-lg-1 d-flex align-items-center mt-2">
<img src="http://placekitten.com/96/96" className="rounded-circle" />
<div className="ms-2">
<strong>Ben Dover</strong><br />
<select className="form-select form-select-sm" aria-label="Default select example">
<option value="1">Consumer</option>
<option value="2" selected>Maintainer</option>
<option value="3">Owner</option>
</select>
</div>
</div>
<div className="g-col-4 g-col-lg-1 d-flex align-items-center mt-2">
<img src="http://placekitten.com/96/96" className="rounded-circle" />
<div className="ms-2">
<strong>Eline Dover</strong><br />
<select className="form-select form-select-sm" aria-label="Default select example">
<option value="1">Consumer</option>
<option value="2">Maintainer</option>
<option value="3" selected>Owner</option>
</select>
</div>
</div>
<div className="g-col-4 g-col-lg-1 mt-2 d-flex align-items-center justify-content-center rounded-circle"
style={{ width: '96px', height: '96px', background: '#DEDEDE', fontSize: '2rem' }}>
<PersonPlus />
</div>
</div>;
}
@@ -79,6 +79,7 @@
file_ref,
hex::encode(Sha256::digest(crate_bytes)),
metadata.inner.into_owned(),
metadata.meta,
)
.await?;
@@ -106,14 +107,6 @@
#[serde(borrow)]
authors: Vec<Cow<'a, str>>,
#[serde(borrow)]
description: Option<Cow<'a, str>>,
#[serde(borrow)]
documentation: Option<Cow<'a, str>>,
#[serde(borrow)]
homepage: Option<Cow<'a, str>>,
#[serde(borrow)]
readme: Option<Cow<'a, str>>,
#[serde(borrow)]
readme_file: Option<Cow<'a, str>>,
#[serde(borrow)]
keywords: Vec<Cow<'a, str>>,
@@ -123,8 +116,8 @@
license: Option<Cow<'a, str>>,
#[serde(borrow)]
license_file: Option<Cow<'a, str>>,
#[serde(borrow)]
repository: Option<Cow<'a, str>>,
#[serde(flatten)]
meta: chartered_types::cargo::CrateVersionMetadata,
#[serde(flatten)]
inner: chartered_types::cargo::CrateVersion<'a>,
}
@@ -1,10 +1,10 @@
use axum::{extract, Json};
use chartered_db::{
crates::Crate,
users::{User, UserCratePermissionValue as Permission},
ConnectionPool,
};
use chartered_types::cargo::CrateVersion;
use chartered_types::cargo::{CrateVersion, CrateVersionMetadata};
use serde::Serialize;
use std::sync::Arc;
use thiserror::Error;
@@ -48,12 +48,23 @@
Ok(Json(Response {
versions: versions
.into_iter()
.map(|v| v.into_cargo_format(&crate_).into_owned())
.map(|v| {
let (inner, meta) = v.into_cargo_format(&crate_);
ResponseVersion { inner: inner.into_owned(), meta }
})
.collect(),
}))
}
#[derive(Serialize)]
pub struct ResponseVersion {
#[serde(flatten)]
meta: CrateVersionMetadata,
#[serde(flatten)]
inner: CrateVersion<'static>,
}
#[derive(Serialize)]
pub struct Response {
versions: Vec<CrateVersion<'static>>,
versions: Vec<ResponseVersion>,
}