Skip to content

Commit 7d69c80

Browse files
committed
frontend: Show feature flags in topbar
So far features were stored only in database. Show them in the topbar menu.
1 parent f6fd6cf commit 7d69c80

File tree

6 files changed

+98
-3
lines changed

6 files changed

+98
-3
lines changed

src/db/types.rs

+11
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use failure::_core::fmt::Formatter;
12
use postgres_types::{FromSql, ToSql};
23
use serde::Serialize;
34

@@ -12,4 +13,14 @@ impl Feature {
1213
pub fn new(name: String, subfeatures: Vec<String>) -> Self {
1314
Feature { name, subfeatures }
1415
}
16+
17+
pub fn is_private(&self) -> bool {
18+
self.name.starts_with('_')
19+
}
20+
}
21+
22+
impl std::fmt::Display for Feature {
23+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
24+
write!(f, "{} = {:?}", self.name, self.subfeatures)
25+
}
1526
}

src/test/fakes.rs

+5
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,11 @@ impl<'a> FakeRelease<'a> {
210210
self
211211
}
212212

213+
pub(crate) fn features(mut self, features: HashMap<String, Vec<String>>) -> Self {
214+
self.package.features = features;
215+
self
216+
}
217+
213218
/// Returns the release_id
214219
pub(crate) fn create(self) -> Result<i32, Error> {
215220
use std::fs;

src/web/crate_details.rs

+41
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ impl CrateDetails {
103103
releases.license,
104104
releases.documentation_url,
105105
releases.default_target,
106+
releases.features,
106107
doc_coverage.total_items,
107108
doc_coverage.documented_items,
108109
doc_coverage.total_items_needing_examples,
@@ -148,6 +149,7 @@ impl CrateDetails {
148149
default_target: krate.get("default_target"),
149150
doc_targets: MetaData::parse_doc_targets(krate.get("doc_targets")),
150151
yanked: krate.get("yanked"),
152+
features: MetaData::parse_features(krate.get("features")),
151153
};
152154

153155
let documented_items: Option<i32> = krate.get("documented_items");
@@ -325,6 +327,7 @@ mod tests {
325327
use crate::test::{wrapper, TestDatabase};
326328
use failure::Error;
327329
use kuchiki::traits::TendrilSink;
330+
use std::collections::HashMap;
328331

329332
fn assert_last_successful_build_equals(
330333
db: &TestDatabase,
@@ -741,4 +744,42 @@ mod tests {
741744
Ok(())
742745
});
743746
}
747+
748+
#[test]
749+
fn feature_flags_is_hidden_when_empty() {
750+
wrapper(|env| {
751+
env.fake_release()
752+
.name("binary")
753+
.version("0.1.0")
754+
.binary(true)
755+
.features(HashMap::new())
756+
.create()?;
757+
758+
let page = kuchiki::parse_html()
759+
.one(env.frontend().get("/crate/binary/0.1.0").send()?.text()?);
760+
assert!(page.select_first(r#"a[aria-label="Feature"]"#).is_err());
761+
Ok(())
762+
});
763+
}
764+
765+
#[test]
766+
fn feature_private_feature_flags_are_hidden() {
767+
wrapper(|env| {
768+
let features = [("_private".into(), Vec::new())]
769+
.iter()
770+
.cloned()
771+
.collect::<HashMap<String, Vec<String>>>();
772+
env.fake_release()
773+
.name("binary")
774+
.version("0.1.0")
775+
.binary(true)
776+
.features(features)
777+
.create()?;
778+
779+
let page = kuchiki::parse_html()
780+
.one(env.frontend().get("/crate/binary/0.1.0").send()?.text()?);
781+
assert!(page.select_first(r#"a[aria-label="Feature"]"#).is_err());
782+
Ok(())
783+
});
784+
}
744785
}

src/web/mod.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ mod sitemap;
9090
mod source;
9191
mod statics;
9292

93+
use crate::db::types::Feature;
9394
use crate::{impl_webpage, Context};
9495
use chrono::{DateTime, Utc};
9596
use error::Nope;
@@ -519,6 +520,7 @@ pub(crate) struct MetaData {
519520
pub(crate) default_target: String,
520521
pub(crate) doc_targets: Vec<String>,
521522
pub(crate) yanked: bool,
523+
pub(crate) features: Option<Vec<String>>,
522524
}
523525

524526
impl MetaData {
@@ -532,7 +534,8 @@ impl MetaData {
532534
releases.rustdoc_status,
533535
releases.default_target,
534536
releases.doc_targets,
535-
releases.yanked
537+
releases.yanked,
538+
releases.features
536539
FROM releases
537540
INNER JOIN crates ON crates.id = releases.crate_id
538541
WHERE crates.name = $1 AND releases.version = $2",
@@ -551,6 +554,7 @@ impl MetaData {
551554
default_target: row.get(5),
552555
doc_targets: MetaData::parse_doc_targets(row.get(6)),
553556
yanked: row.get(7),
557+
features: MetaData::parse_features(row.get(8)),
554558
})
555559
}
556560

@@ -565,6 +569,15 @@ impl MetaData {
565569
})
566570
.unwrap_or_else(Vec::new)
567571
}
572+
573+
fn parse_features(features: Option<Vec<Feature>>) -> Option<Vec<String>> {
574+
features.map(|vec| {
575+
vec.iter()
576+
.filter(|feature| !feature.is_private())
577+
.map(|feature| feature.to_string())
578+
.collect()
579+
})
580+
}
568581
}
569582

570583
#[derive(Debug, Clone, PartialEq, Serialize)]
@@ -843,6 +856,7 @@ mod test {
843856
"arm64-unknown-linux-gnu".to_string(),
844857
],
845858
yanked: false,
859+
features: None,
846860
};
847861

848862
let correct_json = json!({
@@ -857,6 +871,7 @@ mod test {
857871
"arm64-unknown-linux-gnu",
858872
],
859873
"yanked": false,
874+
"features": null
860875
});
861876

862877
assert_eq!(correct_json, serde_json::to_value(&metadata).unwrap());
@@ -874,6 +889,7 @@ mod test {
874889
"arm64-unknown-linux-gnu",
875890
],
876891
"yanked": false,
892+
"features": null,
877893
});
878894

879895
assert_eq!(correct_json, serde_json::to_value(&metadata).unwrap());
@@ -891,6 +907,7 @@ mod test {
891907
"arm64-unknown-linux-gnu",
892908
],
893909
"yanked": false,
910+
"features": null,
894911
});
895912

896913
assert_eq!(correct_json, serde_json::to_value(&metadata).unwrap());

src/web/source.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ impl FileList {
5858
releases.files,
5959
releases.default_target,
6060
releases.doc_targets,
61-
releases.yanked
61+
releases.yanked,
62+
releases.features
6263
FROM releases
6364
LEFT OUTER JOIN crates ON crates.id = releases.crate_id
6465
WHERE crates.name = $1 AND releases.version = $2",
@@ -137,6 +138,7 @@ impl FileList {
137138
default_target: rows[0].get(6),
138139
doc_targets: MetaData::parse_doc_targets(rows[0].get(7)),
139140
yanked: rows[0].get(8),
141+
features: MetaData::parse_features(rows[0].get(9)),
140142
},
141143
files: file_list,
142144
})

templates/rustdoc/topbar.html

+20-1
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,26 @@
226226
</li>
227227
{%- endfor -%}
228228
</ul>
229-
</li>
229+
</li>{#
230+
Display the features available in current build
231+
#}
232+
{%- if metadata.features -%}
233+
<li class="pure-menu-item pure-menu-has-children">
234+
<a href="#" class="pure-menu-link" aria-label="Features">
235+
{{ "flag" | fas }}
236+
<span class="title">Feature flags</span>
237+
</a>
238+
239+
{# Build the dropdown list showing available features #}
240+
<ul class="pure-menu-children">
241+
{%- for feature in metadata.features -%}
242+
<li class="pure-menu-item">
243+
<span class="pure-menu-link">{{ feature }}</span>
244+
</li>
245+
{%- endfor -%}
246+
</ul>
247+
</li>
248+
{%- endif -%}
230249
</ul>
231250

232251
{%- include "header/topbar_end.html" -%}

0 commit comments

Comments
 (0)