Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app/components/crate-row.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@
{{#if @crate.homepage}}
<li><a href="{{@crate.homepage}}">Homepage</a></li>
{{/if}}
{{#if @crate.documentation}}
<li><a href="{{@crate.documentation}}">Documentation</a></li>
{{#if @crate.documentationLink}}
<li><a href="{{@crate.documentationLink}}">Documentation</a></li>
{{/if}}
{{#if @crate.repository}}
<li><a href="{{@crate.repository}}">Repository</a></li>
{{/if}}
</ul>

</div>
</div>
21 changes: 19 additions & 2 deletions app/controllers/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import { tracked } from '@glimmer/tracking';

import { restartableTask } from 'ember-concurrency';
import { all, restartableTask } from 'ember-concurrency';
import { bool, reads } from 'macro-decorators';

import { AjaxError } from '../utils/ajax';
import { pagination } from '../utils/pagination';
import { CATEGORY_PREFIX, processSearchQuery } from '../utils/search';

Expand Down Expand Up @@ -73,6 +74,22 @@ export default class SearchController extends Controller {
? { page, per_page, sort, q: query, all_keywords }
: { page, per_page, sort, ...processSearchQuery(query) };

return await this.store.query('crate', searchOptions);
const crates = await this.store.query('crate', searchOptions);

// Prime the docs for the most recent versions of each crate.
const docTasks = [];
for (const crate of crates) {
docTasks.push(crate.loadDocsStatusTask.perform());
}
try {
await all(docTasks);
} catch (e) {
// report unexpected errors to Sentry and ignore `ajax()` errors
if (!didCancel(error) && !(error instanceof AjaxError)) {
this.sentry.captureException(error);
}
}

return crates;
});
}
42 changes: 42 additions & 0 deletions app/models/crate.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import Model, { attr, hasMany } from '@ember-data/model';
import { waitForPromise } from '@ember/test-waiters';

import { task } from 'ember-concurrency';
import { apiAction } from '@mainmatter/ember-api-actions';
import { cached } from 'tracked-toolbox';

import ajax from '../utils/ajax';

export default class Crate extends Model {
@attr name;
@attr downloads;
Expand Down Expand Up @@ -42,6 +45,45 @@ export default class Crate extends Model {
}
}

get documentationLink() {
let crateDocsLink = this.documentation;

// if this is *not* a docs.rs link we'll return it directly
if (crateDocsLink && !crateDocsLink.startsWith('https://docs.rs/')) {
return crateDocsLink;
}

// if we know about a successful docs.rs build, we'll return a link to that
let { docsRsLink } = this;
if (docsRsLink) {
return docsRsLink;
}

// finally, we'll return the specified documentation link, whatever it is
if (crateDocsLink) {
return crateDocsLink;
}

return null;
}

loadDocsStatusTask = task(async () => {
if (!this.documentation) {
return await ajax(`https://docs.rs/crate/${this.name}/=${this.defaultVersion}/status.json`);
}
});

get hasDocsRsLink() {
let docsStatus = this.loadDocsStatusTask.lastSuccessful?.value;
return docsStatus?.doc_status === true;
}

get docsRsLink() {
if (this.hasDocsRsLink) {
return `https://docs.rs/${this.name}`;
}
}

@cached get versionIdsBySemver() {
let versions = this.versions.toArray() ?? [];
return versions.sort(compareVersionBySemver).map(v => v.id);
Expand Down