Skip to content

Commit

Permalink
frontend: Show feature flags in topbar
Browse files Browse the repository at this point in the history
So far features were stored only in database.
Show them in the topbar menu.
  • Loading branch information
almusil committed Oct 30, 2020
1 parent f6fd6cf commit 7d69c80
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 3 deletions.
11 changes: 11 additions & 0 deletions src/db/types.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use failure::_core::fmt::Formatter;
use postgres_types::{FromSql, ToSql};
use serde::Serialize;

Expand All @@ -12,4 +13,14 @@ impl Feature {
pub fn new(name: String, subfeatures: Vec<String>) -> Self {
Feature { name, subfeatures }
}

pub fn is_private(&self) -> bool {
self.name.starts_with('_')
}
}

impl std::fmt::Display for Feature {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{} = {:?}", self.name, self.subfeatures)
}
}
5 changes: 5 additions & 0 deletions src/test/fakes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,11 @@ impl<'a> FakeRelease<'a> {
self
}

pub(crate) fn features(mut self, features: HashMap<String, Vec<String>>) -> Self {
self.package.features = features;
self
}

/// Returns the release_id
pub(crate) fn create(self) -> Result<i32, Error> {
use std::fs;
Expand Down
41 changes: 41 additions & 0 deletions src/web/crate_details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ impl CrateDetails {
releases.license,
releases.documentation_url,
releases.default_target,
releases.features,
doc_coverage.total_items,
doc_coverage.documented_items,
doc_coverage.total_items_needing_examples,
Expand Down Expand Up @@ -148,6 +149,7 @@ impl CrateDetails {
default_target: krate.get("default_target"),
doc_targets: MetaData::parse_doc_targets(krate.get("doc_targets")),
yanked: krate.get("yanked"),
features: MetaData::parse_features(krate.get("features")),
};

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

fn assert_last_successful_build_equals(
db: &TestDatabase,
Expand Down Expand Up @@ -741,4 +744,42 @@ mod tests {
Ok(())
});
}

#[test]
fn feature_flags_is_hidden_when_empty() {
wrapper(|env| {
env.fake_release()
.name("binary")
.version("0.1.0")
.binary(true)
.features(HashMap::new())
.create()?;

let page = kuchiki::parse_html()
.one(env.frontend().get("/crate/binary/0.1.0").send()?.text()?);
assert!(page.select_first(r#"a[aria-label="Feature"]"#).is_err());
Ok(())
});
}

#[test]
fn feature_private_feature_flags_are_hidden() {
wrapper(|env| {
let features = [("_private".into(), Vec::new())]
.iter()
.cloned()
.collect::<HashMap<String, Vec<String>>>();
env.fake_release()
.name("binary")
.version("0.1.0")
.binary(true)
.features(features)
.create()?;

let page = kuchiki::parse_html()
.one(env.frontend().get("/crate/binary/0.1.0").send()?.text()?);
assert!(page.select_first(r#"a[aria-label="Feature"]"#).is_err());
Ok(())
});
}
}
19 changes: 18 additions & 1 deletion src/web/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ mod sitemap;
mod source;
mod statics;

use crate::db::types::Feature;
use crate::{impl_webpage, Context};
use chrono::{DateTime, Utc};
use error::Nope;
Expand Down Expand Up @@ -519,6 +520,7 @@ pub(crate) struct MetaData {
pub(crate) default_target: String,
pub(crate) doc_targets: Vec<String>,
pub(crate) yanked: bool,
pub(crate) features: Option<Vec<String>>,
}

impl MetaData {
Expand All @@ -532,7 +534,8 @@ impl MetaData {
releases.rustdoc_status,
releases.default_target,
releases.doc_targets,
releases.yanked
releases.yanked,
releases.features
FROM releases
INNER JOIN crates ON crates.id = releases.crate_id
WHERE crates.name = $1 AND releases.version = $2",
Expand All @@ -551,6 +554,7 @@ impl MetaData {
default_target: row.get(5),
doc_targets: MetaData::parse_doc_targets(row.get(6)),
yanked: row.get(7),
features: MetaData::parse_features(row.get(8)),
})
}

Expand All @@ -565,6 +569,15 @@ impl MetaData {
})
.unwrap_or_else(Vec::new)
}

fn parse_features(features: Option<Vec<Feature>>) -> Option<Vec<String>> {
features.map(|vec| {
vec.iter()
.filter(|feature| !feature.is_private())
.map(|feature| feature.to_string())
.collect()
})
}
}

#[derive(Debug, Clone, PartialEq, Serialize)]
Expand Down Expand Up @@ -843,6 +856,7 @@ mod test {
"arm64-unknown-linux-gnu".to_string(),
],
yanked: false,
features: None,
};

let correct_json = json!({
Expand All @@ -857,6 +871,7 @@ mod test {
"arm64-unknown-linux-gnu",
],
"yanked": false,
"features": null
});

assert_eq!(correct_json, serde_json::to_value(&metadata).unwrap());
Expand All @@ -874,6 +889,7 @@ mod test {
"arm64-unknown-linux-gnu",
],
"yanked": false,
"features": null,
});

assert_eq!(correct_json, serde_json::to_value(&metadata).unwrap());
Expand All @@ -891,6 +907,7 @@ mod test {
"arm64-unknown-linux-gnu",
],
"yanked": false,
"features": null,
});

assert_eq!(correct_json, serde_json::to_value(&metadata).unwrap());
Expand Down
4 changes: 3 additions & 1 deletion src/web/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ impl FileList {
releases.files,
releases.default_target,
releases.doc_targets,
releases.yanked
releases.yanked,
releases.features
FROM releases
LEFT OUTER JOIN crates ON crates.id = releases.crate_id
WHERE crates.name = $1 AND releases.version = $2",
Expand Down Expand Up @@ -137,6 +138,7 @@ impl FileList {
default_target: rows[0].get(6),
doc_targets: MetaData::parse_doc_targets(rows[0].get(7)),
yanked: rows[0].get(8),
features: MetaData::parse_features(rows[0].get(9)),
},
files: file_list,
})
Expand Down
21 changes: 20 additions & 1 deletion templates/rustdoc/topbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,26 @@
</li>
{%- endfor -%}
</ul>
</li>
</li>{#
Display the features available in current build
#}
{%- if metadata.features -%}
<li class="pure-menu-item pure-menu-has-children">
<a href="#" class="pure-menu-link" aria-label="Features">
{{ "flag" | fas }}
<span class="title">Feature flags</span>
</a>

{# Build the dropdown list showing available features #}
<ul class="pure-menu-children">
{%- for feature in metadata.features -%}
<li class="pure-menu-item">
<span class="pure-menu-link">{{ feature }}</span>
</li>
{%- endfor -%}
</ul>
</li>
{%- endif -%}
</ul>

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

0 comments on commit 7d69c80

Please sign in to comment.