From 9c1986db6532f0a5de4d57224bc388a247a1ab71 Mon Sep 17 00:00:00 2001 From: Sky Date: Sat, 6 Jan 2024 15:30:18 -0500 Subject: [PATCH] [crates] MSRV Badge Crates.io MSRV: `/crates/msrv/:crate` Crates.io MSRV (version): `/crates/msrv/:crate/:version` --- services/crates/crates-base.js | 2 + services/crates/crates-msrv.service.js | 74 ++++++++++++++++++++++++++ services/crates/crates-msrv.tester.js | 15 ++++++ 3 files changed, 91 insertions(+) create mode 100644 services/crates/crates-msrv.service.js create mode 100644 services/crates/crates-msrv.tester.js diff --git a/services/crates/crates-base.js b/services/crates/crates-base.js index aa4b7d378d911..86746405d9753 100644 --- a/services/crates/crates-base.js +++ b/services/crates/crates-base.js @@ -13,6 +13,7 @@ const crateSchema = Joi.object({ Joi.object({ downloads: nonNegativeInteger, license: Joi.string().required().allow(null), + rust_version: Joi.string().allow(null), }), ) .min(1) @@ -24,6 +25,7 @@ const versionSchema = Joi.object({ downloads: nonNegativeInteger, num: Joi.string().required(), license: Joi.string().required().allow(null), + rust_version: Joi.string().allow(null), }).required(), }).required() diff --git a/services/crates/crates-msrv.service.js b/services/crates/crates-msrv.service.js new file mode 100644 index 0000000000000..b66b3477e952c --- /dev/null +++ b/services/crates/crates-msrv.service.js @@ -0,0 +1,74 @@ +import { NotFound, pathParams } from '../index.js' +import { + BaseCratesService, + description as cratesIoDescription, +} from './crates-base.js' + +const description = ` +${cratesIoDescription} + +MSRV is a crate's minimum suppported rust version, +the oldest version of Rust supported by the crate. +See the [Cargo Book](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field) +for more info. +` + +export default class CratesMSRV extends BaseCratesService { + static category = 'platform-support' + static route = { + base: 'crates/msrv', + pattern: ':crate/:version?', + } + + static openApi = { + '/crates/msrv/{crate}': { + get: { + summary: 'Crates.io MSRV', + description, + parameters: pathParams({ + name: 'crate', + example: 'serde', + }), + }, + }, + '/crates/msrv/{crate}/{version}': { + get: { + summary: 'Crates.io MSRV (version)', + description, + parameters: pathParams( + { + name: 'crate', + example: 'serde', + }, + { + name: 'version', + example: '1.0.194', + }, + ), + }, + }, + } + + static defaultBadgeData = { label: 'msrv', color: 'blue' } + + static transform({ errors, version, versions }) { + // crates.io returns a 200 response with an errors object in + // error scenarios, e.g. https://crates.io/api/v1/crates/libc/0.1 + if (errors) { + throw new NotFound({ prettyMessage: errors[0].detail }) + } + + const msrv = version ? version.rust_version : versions[0].rust_version + if (!msrv) { + throw new NotFound({ prettyMessage: 'unknown' }) + } + + return { msrv } + } + + async handle({ crate, version }) { + const json = await this.fetch({ crate, version }) + const { msrv } = this.constructor.transform(json) + return { message: msrv } + } +} diff --git a/services/crates/crates-msrv.tester.js b/services/crates/crates-msrv.tester.js new file mode 100644 index 0000000000000..bc2ae6e35c33a --- /dev/null +++ b/services/crates/crates-msrv.tester.js @@ -0,0 +1,15 @@ +import { createServiceTester } from '../tester.js' +export const t = await createServiceTester() + +// dummy crate i created specifically for this test case +t.create('msrv') + .get('/shields-test-dummy-crate-msrv-3452398210.json') + .expectBadge({ label: 'msrv', message: '1.69' }) + +t.create('msrv (with version)') + .get('/shields-test-dummy-crate-msrv-3452398210/0.69.0.json') + .expectBadge({ label: 'msrv', message: '1.69' }) + +t.create('msrv (not found)') + .get('/not-a-real-package.json') + .expectBadge({ label: 'msrv', message: 'not found' })