Skip to content

Refactored metadata.rs #687

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 15, 2020
Merged
Changes from 1 commit
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
214 changes: 157 additions & 57 deletions src/docbuilder/metadata.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::collections::HashSet;
use std::path::Path;
use toml::Value;
use crate::error::Result;
use failure::err_msg;
use std::collections::HashSet;
use std::path::Path;
use toml::{map::Map, Value};

/// Metadata for custom builds
///
Expand Down Expand Up @@ -83,23 +83,26 @@ impl Metadata {
return Ok(Metadata::from_manifest(manifest_path));
}
}

Err(err_msg("Manifest not found"))
}

fn from_manifest<P: AsRef<Path>>(path: P) -> Metadata {
use std::fs::File;
use std::io::Read;
let mut f = match File::open(path) {
Ok(f) => f,
Err(_) => return Metadata::default(),
use std::{fs::File, io::Read};

let mut file = if let Ok(file) = File::open(path) {
file
} else {
return Metadata::default();
};
let mut s = String::new();
if let Err(_) = f.read_to_string(&mut s) {

let mut meta = String::new();
if file.read_to_string(&mut meta).is_err() {
return Metadata::default();
}
Metadata::from_str(&s)
}

Metadata::from_str(&meta)
}

// This is similar to Default trait but it's private
fn default() -> Metadata {
Expand All @@ -114,56 +117,98 @@ impl Metadata {
}
}


fn from_str(manifest: &str) -> Metadata {
let mut metadata = Metadata::default();

let manifest = match manifest.parse::<Value>() {
Ok(m) => m,
Err(_) => return metadata,
let manifest = if let Ok(manifest) = manifest.parse::<Value>() {
manifest
} else {
return metadata;
};

if let Some(table) = manifest.get("package").and_then(|p| p.as_table())
.and_then(|p| p.get("metadata")).and_then(|p| p.as_table())
.and_then(|p| p.get("docs")).and_then(|p| p.as_table())
.and_then(|p| p.get("rs")).and_then(|p| p.as_table()) {
metadata.features = table.get("features").and_then(|f| f.as_array())
.and_then(|f| f.iter().map(|v| v.as_str().map(|v| v.to_owned())).collect());
metadata.no_default_features = table.get("no-default-features")
.and_then(|v| v.as_bool()).unwrap_or(metadata.no_default_features);
metadata.all_features = table.get("all-features")
.and_then(|v| v.as_bool()).unwrap_or(metadata.all_features);
metadata.default_target = table.get("default-target")
.and_then(|v| v.as_str()).map(|v| v.to_owned());
metadata.targets = table.get("targets").and_then(|f| f.as_array())
.and_then(|f| f.iter().map(|v| v.as_str().map(|v| v.to_owned())).collect());
metadata.rustc_args = table.get("rustc-args").and_then(|f| f.as_array())
.and_then(|f| f.iter().map(|v| v.as_str().map(|v| v.to_owned())).collect());
metadata.rustdoc_args = table.get("rustdoc-args").and_then(|f| f.as_array())
.and_then(|f| f.iter().map(|v| v.as_str().map(|v| v.to_owned())).collect());
}
if let Some(table) = fetch_manifest_tables(&manifest) {
let collect_into_array =
|f: &Vec<Value>| f.iter().map(|v| v.as_str().map(|v| v.to_owned())).collect();

metadata.features = table
.get("features")
.and_then(|f| f.as_array())
.and_then(collect_into_array);

metadata.no_default_features = table
.get("no-default-features")
.and_then(|v| v.as_bool())
.unwrap_or(metadata.no_default_features);

metadata.all_features = table
.get("all-features")
.and_then(|v| v.as_bool())
.unwrap_or(metadata.all_features);

metadata.default_target = table
.get("default-target")
.and_then(|v| v.as_str())
.map(|v| v.to_owned());

metadata.targets = table
.get("targets")
.and_then(|f| f.as_array())
.and_then(collect_into_array);

metadata.rustc_args = table
.get("rustc-args")
.and_then(|f| f.as_array())
.and_then(collect_into_array);

metadata.rustdoc_args = table
.get("rustdoc-args")
.and_then(|f| f.as_array())
.and_then(collect_into_array);
}

metadata
}

pub(super) fn targets(&self) -> BuildTargets<'_> {
use super::rustwide_builder::{HOST_TARGET, TARGETS};

let default_target = self.default_target.as_deref()
let default_target = self
.default_target
.as_deref()
// Use the first element of `targets` if `default_target` is unset and `targets` is non-empty
.or_else(|| self.targets.as_ref().and_then(|targets| targets.iter().next().map(String::as_str)))
.or_else(|| {
self.targets
.as_ref()
.and_then(|targets| targets.iter().next().map(String::as_str))
})
.unwrap_or(HOST_TARGET);

// Let people opt-in to only having specific targets
let mut targets: HashSet<_> = self.targets.as_ref()
let mut targets: HashSet<_> = self
.targets
.as_ref()
.map(|targets| targets.iter().map(String::as_str).collect())
.unwrap_or_else(|| TARGETS.iter().copied().collect());

targets.remove(&default_target);
BuildTargets { default_target, other_targets: targets }
BuildTargets {
default_target,
other_targets: targets,
}
}
}


fn fetch_manifest_tables<'a>(manifest: &'a Value) -> Option<&'a Map<String, Value>> {
manifest
.get("package")?
.as_table()?
.get("metadata")?
.as_table()?
.get("docs")?
.as_table()?
.get("rs")?
.as_table()
}

#[cfg(test)]
mod test {
Expand Down Expand Up @@ -199,7 +244,10 @@ mod test {
assert_eq!(features[0], "feature1".to_owned());
assert_eq!(features[1], "feature2".to_owned());

assert_eq!(metadata.default_target.unwrap(), "x86_64-unknown-linux-gnu".to_owned());
assert_eq!(
metadata.default_target.unwrap(),
"x86_64-unknown-linux-gnu".to_owned()
);

let targets = metadata.targets.expect("should have explicit target");
assert_eq!(targets.len(), 2);
Expand Down Expand Up @@ -229,32 +277,42 @@ mod test {
assert!(metadata.targets.is_none());

// no package.metadata.docs.rs section
let metadata = Metadata::from_str(r#"
let metadata = Metadata::from_str(
r#"
[package]
name = "test"
"#);
"#,
);
assert!(metadata.targets.is_none());

// targets explicitly set to empty array
let metadata = Metadata::from_str(r#"
let metadata = Metadata::from_str(
r#"
[package.metadata.docs.rs]
targets = []
"#);
"#,
);
assert!(metadata.targets.unwrap().is_empty());
}
#[test]
fn test_select_targets() {
use crate::docbuilder::rustwide_builder::{HOST_TARGET, TARGETS};
use super::BuildTargets;
use crate::docbuilder::rustwide_builder::{HOST_TARGET, TARGETS};

let mut metadata = Metadata::default();

// unchanged default_target, targets not specified
let BuildTargets { default_target: default, other_targets: tier_one } = metadata.targets();
let BuildTargets {
default_target: default,
other_targets: tier_one,
} = metadata.targets();
assert_eq!(default, HOST_TARGET);

// should be equal to TARGETS \ {HOST_TARGET}
for actual in &tier_one {
assert!(TARGETS.contains(actual));
}

for expected in TARGETS {
if *expected == HOST_TARGET {
assert!(!tier_one.contains(&HOST_TARGET));
Expand All @@ -265,47 +323,89 @@ mod test {

// unchanged default_target, targets specified to be empty
metadata.targets = Some(Vec::new());
let BuildTargets { default_target: default, other_targets: others } = metadata.targets();

let BuildTargets {
default_target: default,
other_targets: others,
} = metadata.targets();

assert_eq!(default, HOST_TARGET);
assert!(others.is_empty());

// unchanged default_target, targets non-empty
metadata.targets = Some(vec!["i686-pc-windows-msvc".into(), "i686-apple-darwin".into()]);
let BuildTargets { default_target: default, other_targets: others } = metadata.targets();
metadata.targets = Some(vec![
"i686-pc-windows-msvc".into(),
"i686-apple-darwin".into(),
]);

let BuildTargets {
default_target: default,
other_targets: others,
} = metadata.targets();

assert_eq!(default, "i686-pc-windows-msvc");
assert_eq!(others.len(), 1);
assert!(others.contains(&"i686-apple-darwin"));

// make sure that default_target is not built twice
metadata.targets = Some(vec![HOST_TARGET.into()]);
let BuildTargets { default_target: default, other_targets: others } = metadata.targets();
let BuildTargets {
default_target: default,
other_targets: others,
} = metadata.targets();

assert_eq!(default, HOST_TARGET);
assert!(others.is_empty());

// make sure that duplicates are removed
metadata.targets = Some(vec!["i686-pc-windows-msvc".into(), "i686-pc-windows-msvc".into()]);
let BuildTargets { default_target: default, other_targets: others } = metadata.targets();
metadata.targets = Some(vec![
"i686-pc-windows-msvc".into(),
"i686-pc-windows-msvc".into(),
]);

let BuildTargets {
default_target: default,
other_targets: others,
} = metadata.targets();

assert_eq!(default, "i686-pc-windows-msvc");
assert!(others.is_empty());

// make sure that `default_target` always takes priority over `targets`
metadata.default_target = Some("i686-apple-darwin".into());
let BuildTargets { default_target: default, other_targets: others } = metadata.targets();
let BuildTargets {
default_target: default,
other_targets: others,
} = metadata.targets();

assert_eq!(default, "i686-apple-darwin");
assert_eq!(others.len(), 1);
assert!(others.contains(&"i686-pc-windows-msvc"));

// make sure that `default_target` takes priority over `HOST_TARGET`
metadata.targets = Some(vec![]);
let BuildTargets { default_target: default, other_targets: others } = metadata.targets();
let BuildTargets {
default_target: default,
other_targets: others,
} = metadata.targets();

assert_eq!(default, "i686-apple-darwin");
assert!(others.is_empty());

// and if `targets` is unset, it should still be set to `TARGETS`
metadata.targets = None;
let BuildTargets { default_target: default, other_targets: others } = metadata.targets();
let BuildTargets {
default_target: default,
other_targets: others,
} = metadata.targets();

assert_eq!(default, "i686-apple-darwin");
let tier_one_targets_no_default = TARGETS.iter().filter(|&&t| t != "i686-apple-darwin").copied().collect();
let tier_one_targets_no_default = TARGETS
.iter()
.filter(|&&t| t != "i686-apple-darwin")
.copied()
.collect();

assert_eq!(others, tier_one_targets_no_default);
}
}