From 75fa1c54debc7eb9977bf916c731588da811a920 Mon Sep 17 00:00:00 2001 From: Ales Musil Date: Tue, 27 Oct 2020 11:47:21 +0100 Subject: [PATCH] Add menu item called Feature flags Feature flags should show all available features for current crate version with default being first is specified. --- Cargo.lock | 23 +++++++++++++++++++---- Cargo.toml | 1 + src/db/add_package.rs | 34 +++++++++++++++++++++++++++++++--- src/db/migrate.rs | 19 ++++++++++++++++++- src/db/mod.rs | 1 + src/db/types.rs | 15 +++++++++++++++ src/test/fakes.rs | 11 +++++++++++ src/utils/cargo_metadata.rs | 2 ++ 8 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 src/db/types.rs diff --git a/Cargo.lock b/Cargo.lock index 11536083f..63c058655 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -265,14 +265,16 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "chrono" -version = "0.4.11" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ + "libc", "num-integer", "num-traits", "serde", "time 0.1.43", + "winapi 0.3.8", ] [[package]] @@ -690,6 +692,7 @@ dependencies = [ "once_cell", "path-slash", "postgres", + "postgres-types", "procfs", "prometheus", "r2d2", @@ -2268,6 +2271,17 @@ dependencies = [ "tokio-postgres", ] +[[package]] +name = "postgres-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c857dd221cb0e7d8414b894a0ce29eae44d453dda0baa132447878e75e701477" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "postgres-protocol" version = "0.5.2" @@ -2288,13 +2302,14 @@ dependencies = [ [[package]] name = "postgres-types" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d14b0a4f433b0e0b565bb0fbc0ac9fc3d79ca338ba265ad0e7eef0f3bcc5e94" +checksum = "cfc08a7d94a80665de4a83942fa8db2fdeaf2f123fc0535e384dc4fff251efae" dependencies = [ "bytes", "chrono", "fallible-iterator", + "postgres-derive", "postgres-protocol", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index b7b6c1f86..460b48d4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ lol_html = "0.2" font-awesome-as-a-crate = { path = "crates/font-awesome-as-a-crate" } dashmap = "3.11.10" string_cache = "0.8.0" +postgres-types = { version = "0.1.3", features = ["derive"] } # Async tokio = { version = "0.2.22", features = ["rt-threaded"] } diff --git a/src/db/add_package.rs b/src/db/add_package.rs index e693bdd7e..bf54ba83c 100644 --- a/src/db/add_package.rs +++ b/src/db/add_package.rs @@ -5,6 +5,7 @@ use std::{ }; use crate::{ + db::types::Feature, docbuilder::{BuildResult, DocCoverage}, error::Result, index::api::{CrateData, CrateOwner, ReleaseData}, @@ -42,6 +43,7 @@ pub(crate) fn add_package_into_database( let dependencies = convert_dependencies(metadata_pkg); let rustdoc = get_rustdoc(metadata_pkg, source_dir).unwrap_or(None); let readme = get_readme(metadata_pkg, source_dir).unwrap_or(None); + let features = get_features(metadata_pkg); let is_library = metadata_pkg.is_library(); let rows = conn.query( @@ -52,12 +54,12 @@ pub(crate) fn add_package_into_database( homepage_url, description, description_long, readme, authors, keywords, have_examples, downloads, files, doc_targets, is_library, doc_rustc_version, - documentation_url, default_target + documentation_url, default_target, features ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, - $19, $20, $21, $22, $23, $24, $25 + $19, $20, $21, $22, $23, $24, $25, $26 ) ON CONFLICT (crate_id, version) DO UPDATE SET release_time = $3, @@ -82,7 +84,8 @@ pub(crate) fn add_package_into_database( is_library = $22, doc_rustc_version = $23, documentation_url = $24, - default_target = $25 + default_target = $25, + features = $26 RETURNING id", &[ &crate_id, @@ -110,6 +113,7 @@ pub(crate) fn add_package_into_database( &res.rustc_version, &metadata_pkg.documentation, &default_target, + &features, ], )?; @@ -213,6 +217,30 @@ fn convert_dependencies(pkg: &MetadataPackage) -> Vec<(String, String, String)> .collect() } +/// Reads features and converts them to Vec with default being first +fn get_features(pkg: &MetadataPackage) -> Vec { + let mut features = Vec::with_capacity(pkg.features.len()); + if let Some(subfeatures) = pkg.features.get("default") { + features.push(Feature::new("default".into(), subfeatures.clone())); + }; + features.extend( + pkg.features + .iter() + .filter(|(name, _)| *name != "default") + .map(|(name, subfeatures)| Feature::new(name.clone(), subfeatures.clone())), + ); + features.extend(get_optional_dependencies(pkg)); + features +} + +fn get_optional_dependencies(pkg: &MetadataPackage) -> Vec { + pkg.dependencies + .iter() + .filter(|dep| dep.optional) + .map(|dep| Feature::new(dep.name.clone(), Vec::new())) + .collect() +} + /// Reads readme if there is any read defined in Cargo.toml of a Package fn get_readme(pkg: &MetadataPackage, source_dir: &Path) -> Result> { let readme_path = source_dir.join(pkg.readme.as_deref().unwrap_or("README.md")); diff --git a/src/db/migrate.rs b/src/db/migrate.rs index 929f57de2..c3a450ed0 100644 --- a/src/db/migrate.rs +++ b/src/db/migrate.rs @@ -457,7 +457,24 @@ pub fn migrate(version: Option, conn: &mut Client) -> CratesfyiResult<( DROP COLUMN total_items_needing_examples, DROP COLUMN items_with_examples; " - ) + ), + migration!( + context, + // version + 19, + // description + "Add features that are available for given release", + // upgrade query + " + CREATE TYPE feature AS (name TEXT, subfeatures TEXT[]); + ALTER TABLE releases ADD COLUMN features feature[]; + ", + // downgrade query + " + ALTER TABLE releases DROP COLUMN features; + DROP TYPE feature; + " + ), ]; for migration in migrations { diff --git a/src/db/mod.rs b/src/db/mod.rs index ecb326404..6545591ac 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -15,3 +15,4 @@ mod delete; pub(crate) mod file; mod migrate; mod pool; +pub(crate) mod types; diff --git a/src/db/types.rs b/src/db/types.rs new file mode 100644 index 000000000..e2a973676 --- /dev/null +++ b/src/db/types.rs @@ -0,0 +1,15 @@ +use postgres_types::{FromSql, ToSql}; +use serde::Serialize; + +#[derive(Debug, Clone, PartialEq, Serialize, FromSql, ToSql)] +#[postgres(name = "feature")] +pub struct Feature { + name: String, + subfeatures: Vec, +} + +impl Feature { + pub fn new(name: String, subfeatures: Vec) -> Self { + Feature { name, subfeatures } + } +} diff --git a/src/test/fakes.rs b/src/test/fakes.rs index 0134943dd..6344e60ec 100644 --- a/src/test/fakes.rs +++ b/src/test/fakes.rs @@ -5,6 +5,7 @@ use crate::storage::Storage; use crate::utils::{Dependency, MetadataPackage, Target}; use chrono::{DateTime, Utc}; use failure::Error; +use std::collections::HashMap; use std::sync::Arc; #[must_use = "FakeRelease does nothing until you call .create()"] @@ -48,11 +49,21 @@ impl<'a> FakeRelease<'a> { name: "fake-dependency".into(), req: "^1.0.0".into(), kind: None, + optional: false, }], targets: vec![Target::dummy_lib("fake_package".into(), None)], readme: None, keywords: vec!["fake".into(), "package".into()], authors: vec!["Fake Person ".into()], + features: [ + ("default".into(), vec!["feature1".into(), "feature3".into()]), + ("feature1".into(), Vec::new()), + ("feature2".into(), vec!["feature1".into()]), + ("feature3".into(), Vec::new()), + ] + .iter() + .cloned() + .collect::>>(), }, build_result: BuildResult { rustc_version: "rustc 2.0.0-nightly (000000000 1970-01-01)".into(), diff --git a/src/utils/cargo_metadata.rs b/src/utils/cargo_metadata.rs index 0894f1ddf..19c274c47 100644 --- a/src/utils/cargo_metadata.rs +++ b/src/utils/cargo_metadata.rs @@ -77,6 +77,7 @@ pub(crate) struct Package { pub(crate) readme: Option, pub(crate) keywords: Vec, pub(crate) authors: Vec, + pub(crate) features: HashMap>, } impl Package { @@ -131,6 +132,7 @@ pub(crate) struct Dependency { pub(crate) name: String, pub(crate) req: String, pub(crate) kind: Option, + pub(crate) optional: bool, } #[derive(Deserialize, Serialize)]