Skip to content

Commit

Permalink
Auto merge of #8277 - tverghis:default-readme, r=alexcrichton
Browse files Browse the repository at this point in the history
Default values for `readme` if not specified

If the a value for `readme` is not specified in Cargo.toml, we will now check for the existence of files named `README.md`, `README.txt` or `README`. If one does exist, the name of that file will be defaulted in to the manifest for the project.

This behavior can be suppressed if `readme` is set to `false`.

Closes #8133
  • Loading branch information
bors committed Jun 9, 2020
2 parents 5eb53f7 + 58ee013 commit 1ec223e
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 12 deletions.
38 changes: 36 additions & 2 deletions src/cargo/util/toml/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,7 @@ pub struct TomlProject {
description: Option<String>,
homepage: Option<String>,
documentation: Option<String>,
readme: Option<String>,
readme: Option<StringOrBool>,
keywords: Option<Vec<String>>,
categories: Option<Vec<String>>,
license: Option<String>,
Expand Down Expand Up @@ -1208,11 +1208,19 @@ impl TomlManifest {
project.links.as_deref(),
project.namespaced_features.unwrap_or(false),
)?;

let readme = readme_for_project(package_root, project);
if let Some(ref r) = readme {
if !package_root.join(r).is_file() {
bail!("readme file with name '{}' was not found", r);
}
};

let metadata = ManifestMetadata {
description: project.description.clone(),
homepage: project.homepage.clone(),
documentation: project.documentation.clone(),
readme: project.readme.clone(),
readme,
authors: project.authors.clone().unwrap_or_default(),
license: project.license.clone(),
license_file: project.license_file.clone(),
Expand Down Expand Up @@ -1523,6 +1531,32 @@ impl TomlManifest {
}
}

/// Returns the name of the README file for a `TomlProject`.
fn readme_for_project(package_root: &Path, project: &TomlProject) -> Option<String> {
match &project.readme {
None => default_readme_from_package_root(package_root),
Some(value) => match value {
StringOrBool::Bool(false) => None,
StringOrBool::Bool(true) => Some("README.md".to_string()),
StringOrBool::String(v) => Some(v.clone()),
},
}
}

const DEFAULT_README_FILES: [&str; 3] = ["README.md", "README.txt", "README"];

/// Checks if a file with any of the default README file names exists in the package root.
/// If so, returns a `String` representing that name.
fn default_readme_from_package_root(package_root: &Path) -> Option<String> {
for &readme_filename in DEFAULT_README_FILES.iter() {
if package_root.join(readme_filename).is_file() {
return Some(readme_filename.to_string());
}
}

None
}

/// Checks a list of build targets, and ensures the target names are unique within a vector.
/// If not, the name of the offending build target is returned.
fn unique_build_targets(targets: &[Target], package_root: &Path) -> Result<(), String> {
Expand Down
6 changes: 6 additions & 0 deletions src/doc/src/reference/manifest.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ will interpret it as Markdown and render it on the crate's page.
readme = "README.md"
```

If no value is specified for this field, and a file named `README.md`,
`README.txt` or `README` exists in the package root, then the name of that
file will be used. You can suppress this behavior by setting this field to
`false`. If the field is set to `true`, a default value of `README.md` will
be assumed.

#### The `homepage` field

The `homepage` field should be a URL to a site that is the home page for your
Expand Down
2 changes: 2 additions & 0 deletions tests/testsuite/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,7 @@ fn package_metadata() {
baz = "quux"
"#,
)
.file("README.md", "")
.file("src/lib.rs", "")
.build();

Expand Down Expand Up @@ -1186,6 +1187,7 @@ fn package_publish() {
publish = ["my-registry"]
"#,
)
.file("README.md", "")
.file("src/lib.rs", "")
.build();

Expand Down
125 changes: 115 additions & 10 deletions tests/testsuite/read_manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
use cargo_test_support::{basic_bin_manifest, main_file, project};

static MANIFEST_OUTPUT: &str = r#"
{
fn manifest_output(readme_value: &str) -> String {
format!(
r#"
{{
"authors": [
"wycats@example.com"
],
"categories": [],
"name":"foo",
"readme": null,
"readme": {},
"repository": null,
"version":"0.5.0",
"id":"foo[..]0.5.0[..](path+file://[..]/foo)",
Expand All @@ -21,19 +23,44 @@ static MANIFEST_OUTPUT: &str = r#"
"edition": "2015",
"source":null,
"dependencies":[],
"targets":[{
"targets":[{{
"kind":["bin"],
"crate_types":["bin"],
"doctest": false,
"edition": "2015",
"name":"foo",
"src_path":"[..]/foo/src/foo.rs"
}],
"features":{},
}}],
"features":{{}},
"manifest_path":"[..]Cargo.toml",
"metadata": null,
"publish": null
}"#;
}}"#,
readme_value
)
}

fn manifest_output_no_readme() -> String {
manifest_output("null")
}

pub fn basic_bin_manifest_with_readme(name: &str, readme_filename: &str) -> String {
format!(
r#"
[package]
name = "{}"
version = "0.5.0"
authors = ["wycats@example.com"]
readme = {}
[[bin]]
name = "{}"
"#,
name, readme_filename, name
)
}

#[cargo_test]
fn cargo_read_manifest_path_to_cargo_toml_relative() {
Expand All @@ -44,7 +71,7 @@ fn cargo_read_manifest_path_to_cargo_toml_relative() {

p.cargo("read-manifest --manifest-path foo/Cargo.toml")
.cwd(p.root().parent().unwrap())
.with_json(MANIFEST_OUTPUT)
.with_json(&manifest_output_no_readme())
.run();
}

Expand All @@ -58,7 +85,7 @@ fn cargo_read_manifest_path_to_cargo_toml_absolute() {
p.cargo("read-manifest --manifest-path")
.arg(p.root().join("Cargo.toml"))
.cwd(p.root().parent().unwrap())
.with_json(MANIFEST_OUTPUT)
.with_json(&manifest_output_no_readme())
.run();
}

Expand Down Expand Up @@ -104,5 +131,83 @@ fn cargo_read_manifest_cwd() {
.file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
.build();

p.cargo("read-manifest").with_json(MANIFEST_OUTPUT).run();
p.cargo("read-manifest")
.with_json(&manifest_output_no_readme())
.run();
}

#[cargo_test]
fn cargo_read_manifest_with_specified_readme() {
let p = project()
.file(
"Cargo.toml",
&basic_bin_manifest_with_readme("foo", r#""SomeReadme.txt""#),
)
.file("SomeReadme.txt", "Sample Project")
.file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
.build();

p.cargo("read-manifest")
.with_json(&manifest_output(&format!(r#""{}""#, "SomeReadme.txt")))
.run();
}

#[cargo_test]
fn cargo_read_manifest_default_readme() {
let readme_filenames = ["README.md", "README.txt", "README"];

for readme in readme_filenames.iter() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest("foo"))
.file(readme, "Sample project")
.file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
.build();

p.cargo("read-manifest")
.with_json(&manifest_output(&format!(r#""{}""#, readme)))
.run();
}
}

#[cargo_test]
fn cargo_read_manifest_suppress_default_readme() {
let p = project()
.file(
"Cargo.toml",
&basic_bin_manifest_with_readme("foo", "false"),
)
.file("README.txt", "Sample project")
.file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
.build();

p.cargo("read-manifest")
.with_json(&manifest_output_no_readme())
.run();
}

// If a file named README.md exists, and `readme = true`, the value `README.md` should be defaulted in.
#[cargo_test]
fn cargo_read_manifest_defaults_readme_if_true() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest_with_readme("foo", "true"))
.file("README.md", "Sample project")
.file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
.build();

p.cargo("read-manifest")
.with_json(&manifest_output(&format!(r#""{}""#, "README.md")))
.run();
}

// If a file named README.md does not exist, and `readme = true`, it should panic.
#[cargo_test]
#[should_panic]
fn cargo_read_manifest_panics_if_default_readme_not_found() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest_with_readme("foo", "true"))
.file("README.txt", "Sample project")
.file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
.build();

p.cargo("read-manifest").run();
}

0 comments on commit 1ec223e

Please sign in to comment.