From bdf84f9f9466923efc439a014b483fd30d8f3289 Mon Sep 17 00:00:00 2001
From: Joey Li <joey@joeyli.dev>
Date: Sun, 4 Aug 2024 14:35:28 +0200
Subject: [PATCH] [Crates] Added crate size badge (#10421)

* add some basic tests for size badge

* add crate size to crates fetch

* add crate size service

* change test to reflect base-10 counting instead

* fix downloads tests to work with new crate_size scheme addition

* move handling of unknown size to handle
---
 services/crates/crates-base.js             |  2 +
 services/crates/crates-downloads.tester.js |  7 ++-
 services/crates/crates-size.service.js     | 59 ++++++++++++++++++++++
 services/crates/crates-size.tester.js      | 19 +++++++
 4 files changed, 86 insertions(+), 1 deletion(-)
 create mode 100644 services/crates/crates-size.service.js
 create mode 100644 services/crates/crates-size.tester.js

diff --git a/services/crates/crates-base.js b/services/crates/crates-base.js
index 079f519de5..7b7571c55d 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 8bc169570e..1f2986e722 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 0000000000..ab819c46ac
--- /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 0000000000..6aea268ced
--- /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' })
-- 
GitLab