Skip to content

Commit 116a17b

Browse files
authored
Merge pull request #11955 from Turbo87/sloc-display
Show SLoC values in crate page sidebars
2 parents 9f0d9db + 50bac71 commit 116a17b

File tree

46 files changed

+606
-11
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+606
-11
lines changed

app/components/crate-sidebar.css

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
.msrv,
2323
.edition,
2424
.license,
25+
.linecount,
2526
.bytes,
2627
.purl {
2728
display: flex;
@@ -37,7 +38,8 @@
3738

3839
.date,
3940
.msrv,
40-
.edition {
41+
.edition,
42+
.linecount {
4143
> span {
4244
cursor: help;
4345
}
@@ -49,6 +51,7 @@
4951
}
5052
}
5153

54+
.linecount,
5255
.bytes {
5356
font-variant-numeric: tabular-nums;
5457
}

app/components/crate-sidebar.gjs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import Tooltip from 'crates-io/components/tooltip';
2121
import dateFormat from 'crates-io/helpers/date-format';
2222
import dateFormatDistanceToNow from 'crates-io/helpers/date-format-distance-to-now';
2323
import dateFormatIso from 'crates-io/helpers/date-format-iso';
24+
import formatShortNum from 'crates-io/helpers/format-short-num';
2425
import prettyBytes from 'crates-io/helpers/pretty-bytes';
2526

2627
import { simplifyUrl } from './crate-sidebar/link';
@@ -125,6 +126,20 @@ export default class CrateSidebar extends Component {
125126
</div>
126127
{{/if}}
127128

129+
{{#if @version.linecounts.total_code_lines}}
130+
<div class='linecount' data-test-linecounts>
131+
{{svgJar 'code'}}
132+
<span>
133+
{{formatShortNum @version.linecounts.total_code_lines}}
134+
SLoC
135+
<Tooltip>
136+
Source Lines of Code<br />
137+
<small>(excluding comments, integration tests and example code)</small>
138+
</Tooltip>
139+
</span>
140+
</div>
141+
{{/if}}
142+
128143
{{#if @version.crate_size}}
129144
<div class='bytes'>
130145
{{svgJar 'weight'}}

app/helpers/format-short-num.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import Helper from '@ember/component/helper';
2+
import { service } from '@ember/service';
3+
4+
/**
5+
* This matches the implementation in https://github.com/rust-lang/crates_io_og_image/blob/v0.2.1/src/formatting.rs
6+
* to ensure that we render roughly the same values in our user interface and the generated OpenGraph images.
7+
*/
8+
export default class FormatShortNumHelper extends Helper {
9+
@service intl;
10+
11+
compute([value]) {
12+
const THRESHOLD = 1500;
13+
const UNITS = ['', 'K', 'M'];
14+
15+
let numValue = Number(value);
16+
let unitIndex = 0;
17+
18+
// Keep dividing by 1000 until value is below threshold or we've reached the last unit
19+
while (numValue >= THRESHOLD && unitIndex < UNITS.length - 1) {
20+
numValue /= 1000;
21+
unitIndex += 1;
22+
}
23+
24+
let unit = UNITS[unitIndex];
25+
26+
// Special case for numbers without suffix - no decimal places
27+
if (unitIndex === 0) {
28+
return this.intl.formatNumber(value);
29+
}
30+
31+
// For K and M, format with appropriate decimal places
32+
// Determine number of decimal places to keep number under 4 chars
33+
let fractionDigits = numValue < 10 ? 1 : 0;
34+
let number = this.intl.formatNumber(numValue, {
35+
minimumFractionDigits: fractionDigits,
36+
maximumFractionDigits: fractionDigits,
37+
});
38+
39+
return number + unit;
40+
}
41+
}

app/models/version.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export default class Version extends Model {
2525
@attr yanked;
2626
@attr license;
2727
@attr crate_size;
28+
@attr linecounts;
2829

2930
/**
3031
* The minimum supported Rust version of this crate version.

app/services/intl.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export default class IntlService extends Service {
44
// `undefined` means "use the default language of the browser"
55
locale = undefined;
66

7-
formatNumber(value) {
8-
return Number(value).toLocaleString(this.locale);
7+
formatNumber(value, options) {
8+
return Number(value).toLocaleString(this.locale, options);
99
}
1010
}

e2e/acceptance/crate.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,18 @@ test.describe('Acceptance | crate page', { tag: '@acceptance' }, () => {
210210
await expect(page.locator('[data-test-license]')).toHaveText('MIT OR Apache-2.0');
211211
});
212212

213+
test('sidebar shows correct information', async ({ page, msw }) => {
214+
let crate = msw.db.crate.create({ name: 'foo' });
215+
msw.db.version.create({ crate, num: '0.5.0' });
216+
msw.db.version.create({ crate, num: '1.0.0' });
217+
218+
await page.goto('/crates/foo');
219+
await expect(page.locator('[data-test-linecounts]')).toHaveText('1,119 SLoC');
220+
221+
await page.goto('/crates/foo/0.5.0');
222+
await expect(page.locator('[data-test-linecounts]')).toHaveText('520 SLoC');
223+
});
224+
213225
test.skip('crates can be yanked by owner', async ({ page, msw }) => {
214226
loadFixtures(msw.db);
215227

packages/crates-io-msw/handlers/crates/downloads.test.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,22 @@ test('includes related versions', async function () {
9191
features: {},
9292
id: 1,
9393
license: 'MIT',
94+
linecounts: {
95+
languages: {
96+
JavaScript: {
97+
code_lines: 325,
98+
comment_lines: 80,
99+
files: 8,
100+
},
101+
TypeScript: {
102+
code_lines: 195,
103+
comment_lines: 10,
104+
files: 2,
105+
},
106+
},
107+
total_code_lines: 520,
108+
total_comment_lines: 90,
109+
},
94110
links: {
95111
dependencies: '/api/v1/crates/rand/1.0.0/dependencies',
96112
version_downloads: '/api/v1/crates/rand/1.0.0/downloads',
@@ -113,6 +129,27 @@ test('includes related versions', async function () {
113129
features: {},
114130
id: 2,
115131
license: 'Apache-2.0',
132+
linecounts: {
133+
languages: {
134+
CSS: {
135+
code_lines: 503,
136+
comment_lines: 42,
137+
files: 2,
138+
},
139+
Python: {
140+
code_lines: 284,
141+
comment_lines: 91,
142+
files: 3,
143+
},
144+
TypeScript: {
145+
code_lines: 332,
146+
comment_lines: 83,
147+
files: 7,
148+
},
149+
},
150+
total_code_lines: 1119,
151+
total_comment_lines: 216,
152+
},
116153
links: {
117154
dependencies: '/api/v1/crates/rand/1.0.1/dependencies',
118155
version_downloads: '/api/v1/crates/rand/1.0.1/downloads',

packages/crates-io-msw/handlers/crates/get.test.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,22 @@ test('returns a crate object for known crates', async function () {
5656
downloads: 3702,
5757
features: {},
5858
license: 'MIT',
59+
linecounts: {
60+
languages: {
61+
JavaScript: {
62+
code_lines: 325,
63+
comment_lines: 80,
64+
files: 8,
65+
},
66+
TypeScript: {
67+
code_lines: 195,
68+
comment_lines: 10,
69+
files: 2,
70+
},
71+
},
72+
total_code_lines: 520,
73+
total_comment_lines: 90,
74+
},
5975
links: {
6076
dependencies: '/api/v1/crates/rand/1.0.0-beta.1/dependencies',
6177
version_downloads: '/api/v1/crates/rand/1.0.0-beta.1/downloads',
@@ -121,6 +137,22 @@ test('works for non-canonical names', async function () {
121137
downloads: 3702,
122138
features: {},
123139
license: 'MIT',
140+
linecounts: {
141+
languages: {
142+
JavaScript: {
143+
code_lines: 325,
144+
comment_lines: 80,
145+
files: 8,
146+
},
147+
TypeScript: {
148+
code_lines: 195,
149+
comment_lines: 10,
150+
files: 2,
151+
},
152+
},
153+
total_code_lines: 520,
154+
total_comment_lines: 90,
155+
},
124156
links: {
125157
dependencies: '/api/v1/crates/foo-bar/1.0.0-beta.1/dependencies',
126158
version_downloads: '/api/v1/crates/foo-bar/1.0.0-beta.1/downloads',
@@ -159,6 +191,17 @@ test('includes related versions', async function () {
159191
downloads: 11_106,
160192
features: {},
161193
license: 'MIT/Apache-2.0',
194+
linecounts: {
195+
languages: {
196+
Python: {
197+
code_lines: 421,
198+
comment_lines: 64,
199+
files: 8,
200+
},
201+
},
202+
total_code_lines: 421,
203+
total_comment_lines: 64,
204+
},
162205
links: {
163206
dependencies: '/api/v1/crates/rand/1.2.0/dependencies',
164207
version_downloads: '/api/v1/crates/rand/1.2.0/downloads',
@@ -181,6 +224,27 @@ test('includes related versions', async function () {
181224
downloads: 7404,
182225
features: {},
183226
license: 'Apache-2.0',
227+
linecounts: {
228+
languages: {
229+
CSS: {
230+
code_lines: 503,
231+
comment_lines: 42,
232+
files: 2,
233+
},
234+
Python: {
235+
code_lines: 284,
236+
comment_lines: 91,
237+
files: 3,
238+
},
239+
TypeScript: {
240+
code_lines: 332,
241+
comment_lines: 83,
242+
files: 7,
243+
},
244+
},
245+
total_code_lines: 1119,
246+
total_comment_lines: 216,
247+
},
184248
links: {
185249
dependencies: '/api/v1/crates/rand/1.1.0/dependencies',
186250
version_downloads: '/api/v1/crates/rand/1.1.0/downloads',
@@ -203,6 +267,22 @@ test('includes related versions', async function () {
203267
downloads: 3702,
204268
features: {},
205269
license: 'MIT',
270+
linecounts: {
271+
languages: {
272+
JavaScript: {
273+
code_lines: 325,
274+
comment_lines: 80,
275+
files: 8,
276+
},
277+
TypeScript: {
278+
code_lines: 195,
279+
comment_lines: 10,
280+
files: 2,
281+
},
282+
},
283+
total_code_lines: 520,
284+
total_comment_lines: 90,
285+
},
206286
links: {
207287
dependencies: '/api/v1/crates/rand/1.0.0/dependencies',
208288
version_downloads: '/api/v1/crates/rand/1.0.0/downloads',

packages/crates-io-msw/handlers/crates/reverse-dependencies.test.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,27 @@ test('returns a paginated list of crate versions depending to the specified crat
7676
downloads: 7404,
7777
features: {},
7878
license: 'Apache-2.0',
79+
linecounts: {
80+
languages: {
81+
CSS: {
82+
code_lines: 503,
83+
comment_lines: 42,
84+
files: 2,
85+
},
86+
Python: {
87+
code_lines: 284,
88+
comment_lines: 91,
89+
files: 3,
90+
},
91+
TypeScript: {
92+
code_lines: 332,
93+
comment_lines: 83,
94+
files: 7,
95+
},
96+
},
97+
total_code_lines: 1119,
98+
total_comment_lines: 216,
99+
},
79100
links: {
80101
dependencies: '/api/v1/crates/baz/1.0.1/dependencies',
81102
version_downloads: '/api/v1/crates/baz/1.0.1/downloads',
@@ -98,6 +119,22 @@ test('returns a paginated list of crate versions depending to the specified crat
98119
downloads: 3702,
99120
features: {},
100121
license: 'MIT',
122+
linecounts: {
123+
languages: {
124+
JavaScript: {
125+
code_lines: 325,
126+
comment_lines: 80,
127+
files: 8,
128+
},
129+
TypeScript: {
130+
code_lines: 195,
131+
comment_lines: 10,
132+
files: 2,
133+
},
134+
},
135+
total_code_lines: 520,
136+
total_comment_lines: 90,
137+
},
101138
links: {
102139
dependencies: '/api/v1/crates/bar/1.0.0/dependencies',
103140
version_downloads: '/api/v1/crates/bar/1.0.0/downloads',

packages/crates-io-msw/handlers/versions/follow-updates.test.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,22 @@ test('returns latest versions of followed crates', async function () {
3333
downloads: 3702,
3434
features: {},
3535
license: 'MIT',
36+
linecounts: {
37+
languages: {
38+
JavaScript: {
39+
code_lines: 325,
40+
comment_lines: 80,
41+
files: 8,
42+
},
43+
TypeScript: {
44+
code_lines: 195,
45+
comment_lines: 10,
46+
files: 2,
47+
},
48+
},
49+
total_code_lines: 520,
50+
total_comment_lines: 90,
51+
},
3652
links: {
3753
dependencies: '/api/v1/crates/foo/1.2.3/dependencies',
3854
version_downloads: '/api/v1/crates/foo/1.2.3/downloads',

0 commit comments

Comments
 (0)