Skip to content

Commit 0125a40

Browse files
Add doc coverage information
1 parent 482d566 commit 0125a40

File tree

8 files changed

+189
-3
lines changed

8 files changed

+189
-3
lines changed

src/db/add_package.rs

+20
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,26 @@ pub(crate) fn add_package_into_database(
130130
Ok(release_id)
131131
}
132132

133+
pub(crate) fn add_doc_coverage(
134+
conn: &mut Client,
135+
release_id: i32,
136+
total_items: i32,
137+
documented_items: i32,
138+
) -> Result<i32> {
139+
debug!("Adding doc coverage into database");
140+
let rows = conn.query(
141+
"INSERT INTO doc_coverage (release_id, total_items, documented_items)
142+
VALUES ($1, $2, $3)
143+
ON CONFLICT (release_id) DO UPDATE
144+
SET
145+
total_items = $2,
146+
documented_items = $3
147+
RETURNING release_id",
148+
&[&release_id, &total_items, &documented_items],
149+
)?;
150+
Ok(rows[0].get(0))
151+
}
152+
133153
/// Adds a build into database
134154
pub(crate) fn add_build_into_database(
135155
conn: &mut Client,

src/db/delete.rs

+7
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const METADATA: &[(&str, &str)] = &[
5454
("keyword_rels", "rid"),
5555
("builds", "rid"),
5656
("compression_rels", "release"),
57+
("doc_coverage", "release_id"),
5758
];
5859

5960
fn delete_version_from_database(conn: &mut Client, name: &str, version: &str) -> Result<(), Error> {
@@ -65,6 +66,12 @@ fn delete_version_from_database(conn: &mut Client, name: &str, version: &str) ->
6566
&[&crate_id, &version],
6667
)?;
6768
}
69+
transaction.execute(
70+
"DELETE FROM doc_coverage WHERE release_id = (
71+
SELECT id FROM releases WHERE crate_id = $1 AND version = $2
72+
)",
73+
&[&crate_id, &version],
74+
)?;
6875
transaction.execute(
6976
"DELETE FROM releases WHERE crate_id = $1 AND version = $2",
7077
&[&crate_id, &version],

src/db/migrate.rs

+17
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,23 @@ pub fn migrate(version: Option<Version>, conn: &mut Client) -> CratesfyiResult<(
381381
-- Nope, this is a pure database fix, no going back.
382382
"
383383
),
384+
migration!(
385+
context,
386+
// version
387+
16,
388+
// description
389+
"Create new table for doc coverage",
390+
// upgrade query
391+
"
392+
CREATE TABLE doc_coverage (
393+
release_id INT NOT NULL REFERENCES releases(id),
394+
total_items INT,
395+
documented_items INT
396+
);
397+
",
398+
// downgrade query
399+
"DROP TABLE doc_coverage;"
400+
),
384401
];
385402

386403
for migration in migrations {

src/db/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
//! Database operations
22
33
pub use self::add_package::update_crate_data_in_database;
4-
pub(crate) use self::add_package::{add_build_into_database, add_package_into_database};
4+
pub(crate) use self::add_package::{
5+
add_build_into_database, add_doc_coverage, add_package_into_database,
6+
};
57
pub use self::delete::{delete_crate, delete_version};
68
pub use self::file::add_path_into_database;
79
pub use self::migrate::migrate;

src/docbuilder/rustwide_builder.rs

+121-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use super::Metadata;
33
use crate::db::blacklist::is_blacklisted;
44
use crate::db::file::add_path_into_database;
55
use crate::db::{
6-
add_build_into_database, add_package_into_database, update_crate_data_in_database, Pool,
6+
add_build_into_database, add_doc_coverage, add_package_into_database,
7+
update_crate_data_in_database, Pool,
78
};
89
use crate::docbuilder::{crates::crates_from_path, Limits};
910
use crate::error::Result;
@@ -428,6 +429,12 @@ impl RustwideBuilder {
428429
algs,
429430
)?;
430431

432+
if let (Some(total), Some(documented)) =
433+
(res.result.total_items, res.result.documented_items)
434+
{
435+
add_doc_coverage(&mut conn, release_id, total, documented)?;
436+
}
437+
431438
add_build_into_database(&mut conn, release_id, &res.result)?;
432439

433440
// Some crates.io crate data is mutable, so we proactively update it during a release
@@ -469,6 +476,106 @@ impl RustwideBuilder {
469476
Ok(())
470477
}
471478

479+
fn get_coverage(
480+
&self,
481+
target: &str,
482+
build: &Build,
483+
metadata: &Metadata,
484+
limits: &Limits,
485+
) -> Option<(i32, i32)> {
486+
let rustdoc_flags: Vec<String> = vec![
487+
"-Z".to_string(),
488+
"unstable-options".to_string(),
489+
"--static-root-path".to_string(),
490+
"/".to_string(),
491+
"--cap-lints".to_string(),
492+
"warn".to_string(),
493+
"--output-format".to_string(),
494+
"json".to_string(),
495+
"--show-coverage".to_string(),
496+
];
497+
498+
let mut cargo_args = vec!["doc", "--lib", "--no-deps"];
499+
if target != HOST_TARGET {
500+
// If the explicit target is not a tier one target, we need to install it.
501+
if !TARGETS.contains(&target) {
502+
// This is a no-op if the target is already installed.
503+
self.toolchain.add_target(&self.workspace, target).ok()?;
504+
}
505+
cargo_args.push("--target");
506+
cargo_args.push(target);
507+
};
508+
509+
let tmp_jobs;
510+
if let Some(cpu_limit) = self.cpu_limit {
511+
tmp_jobs = format!("-j{}", cpu_limit);
512+
cargo_args.push(&tmp_jobs);
513+
}
514+
515+
let tmp;
516+
if let Some(features) = &metadata.features {
517+
cargo_args.push("--features");
518+
tmp = features.join(" ");
519+
cargo_args.push(&tmp);
520+
}
521+
if metadata.all_features {
522+
cargo_args.push("--all-features");
523+
}
524+
if metadata.no_default_features {
525+
cargo_args.push("--no-default-features");
526+
}
527+
528+
let mut storage = LogStorage::new(LevelFilter::Info);
529+
storage.set_max_size(limits.max_log_size());
530+
531+
let mut json = String::new();
532+
if build
533+
.cargo()
534+
.timeout(Some(limits.timeout()))
535+
.no_output_timeout(None)
536+
.env(
537+
"RUSTFLAGS",
538+
metadata
539+
.rustc_args
540+
.as_ref()
541+
.map(|args| args.join(" "))
542+
.unwrap_or_default(),
543+
)
544+
.env("RUSTDOCFLAGS", rustdoc_flags.join(" "))
545+
// For docs.rs detection from build script:
546+
// https://github.com/rust-lang/docs.rs/issues/147
547+
.env("DOCS_RS", "1")
548+
.args(&cargo_args)
549+
.log_output(false)
550+
.process_lines(&mut |line, _| {
551+
if line.starts_with('{') && line.ends_with('}') {
552+
json = line.to_owned();
553+
}
554+
})
555+
.run()
556+
.is_ok()
557+
{
558+
match serde_json::from_str(&json).expect("conversion failed...") {
559+
Value::Object(m) => {
560+
let mut total = 0;
561+
let mut documented = 0;
562+
for entry in m.values() {
563+
if let Some(Value::Number(n)) = entry.get("total") {
564+
total += n.as_i64().unwrap_or(0) as i32;
565+
}
566+
if let Some(Value::Number(n)) = entry.get("with_docs") {
567+
documented += n.as_i64().unwrap_or(0) as i32;
568+
}
569+
}
570+
Some((total, documented))
571+
}
572+
_ => None,
573+
}
574+
} else {
575+
None
576+
}
577+
}
578+
472579
fn execute_build(
473580
&self,
474581
target: &str,
@@ -556,6 +663,14 @@ impl RustwideBuilder {
556663
.run()
557664
.is_ok()
558665
});
666+
let mut total_items = None;
667+
let mut documented_items = None;
668+
if successful {
669+
if let Some((total, documented)) = self.get_coverage(target, build, metadata, limits) {
670+
total_items = Some(total);
671+
documented_items = Some(documented);
672+
}
673+
}
559674
// If we're passed a default_target which requires a cross-compile,
560675
// cargo will put the output in `target/<target>/doc`.
561676
// However, if this is the default build, we don't want it there,
@@ -575,6 +690,8 @@ impl RustwideBuilder {
575690
rustc_version: self.rustc_version.clone(),
576691
docsrs_version: format!("docsrs {}", crate::BUILD_VERSION),
577692
successful,
693+
total_items,
694+
documented_items,
578695
},
579696
cargo_metadata,
580697
target: target.to_string(),
@@ -631,4 +748,7 @@ pub(crate) struct BuildResult {
631748
pub(crate) docsrs_version: String,
632749
pub(crate) build_log: String,
633750
pub(crate) successful: bool,
751+
// The two next fields are used for the doc coverage.
752+
pub(crate) total_items: Option<i32>,
753+
pub(crate) documented_items: Option<i32>,
634754
}

src/test/fakes.rs

+2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ impl<'a> FakeRelease<'a> {
5959
docsrs_version: "docs.rs 1.0.0 (000000000 1970-01-01)".into(),
6060
build_log: "It works!".into(),
6161
successful: true,
62+
total_items: None,
63+
documented_items: None,
6264
},
6365
source_files: Vec::new(),
6466
rustdoc_files: Vec::new(),

src/web/crate_details.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub struct CrateDetails {
4444
pub(crate) doc_targets: Vec<String>,
4545
license: Option<String>,
4646
documentation_url: Option<String>,
47+
doc_coverage: Option<f32>,
4748
}
4849

4950
fn optional_markdown<S>(markdown: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
@@ -97,9 +98,12 @@ impl CrateDetails {
9798
releases.doc_targets,
9899
releases.license,
99100
releases.documentation_url,
100-
releases.default_target
101+
releases.default_target,
102+
doc_coverage.total_items,
103+
doc_coverage.documented_items
101104
FROM releases
102105
INNER JOIN crates ON releases.crate_id = crates.id
106+
LEFT JOIN doc_coverage ON doc_coverage.release_id = releases.id
103107
WHERE crates.name = $1 AND releases.version = $2;";
104108

105109
let rows = conn.query(query, &[&name, &version]).unwrap();
@@ -180,6 +184,16 @@ impl CrateDetails {
180184
doc_targets,
181185
license: krate.get("license"),
182186
documentation_url: krate.get("documentation_url"),
187+
doc_coverage: {
188+
let total: Option<i32> = krate.get("total_items");
189+
let documented: Option<i32> = krate.get("documented_items");
190+
match (total, documented) {
191+
(Some(total), Some(documented)) => {
192+
Some(documented as f32 * 100. / total as f32)
193+
}
194+
_ => None,
195+
}
196+
},
183197
};
184198

185199
if let Some(repository_url) = crate_details.repository_url.clone() {

templates/crate/details.html

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
<div class="pure-u-1 pure-u-sm-7-24 pure-u-md-5-24">
1717
<div class="pure-menu package-menu">
1818
<ul class="pure-menu-list">
19+
{%- if details.doc_coverage -%}
20+
<li class="pure-menu-heading">Documentation<br>coverage</li>
21+
<li class="pure-menu-item" style="text-align:center;">{{ details.doc_coverage | round(precision=2) }} %</li>
22+
{%- endif -%}
1923
{# List the release author's names and a link to their docs.rs profile #}
2024
<li class="pure-menu-heading">Authors</li>
2125
{%- for author in details.authors -%}

0 commit comments

Comments
 (0)