diff --git a/services/crates/crates-base.js b/services/crates/crates-base.js index 079f519de5482..7b7571c55d029 100644 --- a/services/crates/crates-base.js +++ b/services/crates/crates-base.js @@ -4,6 +4,8 @@ import { BaseJsonService, InvalidResponse } from '../index.js' const versionSchema = Joi.object({ downloads: nonNegativeInteger, + // Crate size is not available for all versions. + crate_size: nonNegativeInteger.allow(null), num: Joi.string().required(), license: Joi.string().required().allow(null), rust_version: Joi.string().allow(null), diff --git a/services/crates/crates-downloads.tester.js b/services/crates/crates-downloads.tester.js index 8bc169570e5fb..1f2986e722e13 100644 --- a/services/crates/crates-downloads.tester.js +++ b/services/crates/crates-downloads.tester.js @@ -42,7 +42,12 @@ t.create('recent downloads (null)') max_version: '0.2.71', }, versions: [ - { downloads: 42, license: 'MIT OR Apache-2.0', num: '0.2.71' }, + { + downloads: 42, + license: 'MIT OR Apache-2.0', + num: '0.2.71', + crate_size: 42, + }, ], }), ) diff --git a/services/crates/crates-size.service.js b/services/crates/crates-size.service.js new file mode 100644 index 0000000000000..ab819c46ac4ae --- /dev/null +++ b/services/crates/crates-size.service.js @@ -0,0 +1,59 @@ +import prettyBytes from 'pretty-bytes' +import { InvalidResponse, pathParams } from '../index.js' +import { BaseCratesService, description } from './crates-base.js' + +export default class CratesSize extends BaseCratesService { + static category = 'size' + static route = { + base: 'crates/size', + pattern: ':crate/:version?', + } + + static openApi = { + '/crates/size/{crate}': { + get: { + summary: 'Crates.io Size', + description, + parameters: pathParams({ + name: 'crate', + example: 'rustc-serialize', + }), + }, + }, + '/crates/size/{crate}/{version}': { + get: { + summary: 'Crates.io Size (version)', + description, + parameters: pathParams( + { + name: 'crate', + example: 'rustc-serialize', + }, + { + name: 'version', + example: '0.3.24', + }, + ), + }, + }, + } + + render({ size }) { + return { + label: 'size', + message: prettyBytes(size), + color: 'blue', + } + } + + async handle({ crate, version }) { + const json = await this.fetch({ crate, version }) + const size = this.constructor.getVersionObj(json).crate_size + + if (size == null) { + throw new InvalidResponse({ prettyMessage: 'unknown' }) + } + + return this.render({ size }) + } +} diff --git a/services/crates/crates-size.tester.js b/services/crates/crates-size.tester.js new file mode 100644 index 0000000000000..6aea268ced66d --- /dev/null +++ b/services/crates/crates-size.tester.js @@ -0,0 +1,19 @@ +import { createServiceTester } from '../tester.js' +import { isFileSize } from '../test-validators.js' +export const t = await createServiceTester() + +t.create('size') + .get('/tokio.json') + .expectBadge({ label: 'size', message: isFileSize }) + +t.create('size (with version)') + .get('/tokio/1.32.0.json') + .expectBadge({ label: 'size', message: '725 kB' }) + +t.create('size (with version where version doesnt have size)') + .get('/tokio/0.1.6.json') + .expectBadge({ label: 'crates.io', message: 'unknown' }) + +t.create('size (not found)') + .get('/not-a-crate.json') + .expectBadge({ label: 'crates.io', message: 'not found' })