Skip to content

Commit 630e151

Browse files
Merge #1134
1134: Record readme filename; use to determine whether to render as Markdown or not r=carols10cents Fixes #995. Depends on rust-lang/cargo#4633 to have the desired effect, but won't break in the absence of that PR; we'll just assume the readme is called `"README.md"` if the client doesn't report one. Small CSS fix included to wrap extra long lines. Comparison of render of https://crates.io/crates/relm's README: | Before | After | | :-: | :-: | | ![screen shot 2017-10-17 at 5 51 50 pm](https://user-images.githubusercontent.com/1915/31650690-42018c7a-b364-11e7-95f8-7e1b8e8d56a0.png) | ![screen shot 2017-10-17 at 5 51 51 pm](https://user-images.githubusercontent.com/1915/31650698-44d16588-b364-11e7-92cf-1c3201c0e60e.png) |
2 parents 566c7be + 931427f commit 630e151

File tree

14 files changed

+88
-13
lines changed

14 files changed

+88
-13
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ oauth2 = "0.3"
4242
log = "0.3"
4343
env_logger = "0.4"
4444
hex = "0.2"
45+
htmlescape = "0.3.1"
4546
license-exprs = "^1.3"
4647
dotenv = "0.10.0"
4748
toml = "0.4"

app/styles/crate.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@
305305
}
306306
.crate-readme {
307307
line-height: 1.5;
308+
overflow-wrap: break-word;
308309

309310
img {
310311
max-width: 100%;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE crates DROP COLUMN readme_file;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE crates ADD COLUMN readme_file VARCHAR;

src/bin/render-readmes.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Iterates over every crate versions ever uploaded and (re-)renders their
2-
// readme using the Markdown renderer from the cargo_registry crate.
2+
// readme using the readme renderer from the cargo_registry crate.
33
//
44
// Warning: this can take a lot of time.
55

@@ -34,7 +34,7 @@ use url::Url;
3434

3535
use cargo_registry::{Config, Version};
3636
use cargo_registry::schema::*;
37-
use cargo_registry::render::markdown_to_html;
37+
use cargo_registry::render::readme_to_html;
3838

3939
const DEFAULT_PAGE_SIZE: usize = 25;
4040
const USAGE: &'static str = "
@@ -255,7 +255,7 @@ fn get_readme(config: &Config, version: &Version, krate_name: &str) -> Option<St
255255
manifest.package.readme.unwrap()
256256
);
257257
let contents = find_file_by_path(&mut entries, Path::new(&path), &version, &krate_name);
258-
markdown_to_html(&contents).expect(&format!(
258+
readme_to_html(&contents, &path).expect(&format!(
259259
"[{}-{}] Couldn't render README",
260260
krate_name,
261261
version.num

src/krate/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ pub struct Crate {
4949
pub homepage: Option<String>,
5050
pub documentation: Option<String>,
5151
pub readme: Option<String>,
52+
pub readme_file: Option<String>,
5253
pub license: Option<String>,
5354
pub repository: Option<String>,
5455
pub max_upload_size: Option<i32>,
@@ -66,6 +67,7 @@ type AllColumns = (
6667
crates::homepage,
6768
crates::documentation,
6869
crates::readme,
70+
crates::readme_file,
6971
crates::license,
7072
crates::repository,
7173
crates::max_upload_size,
@@ -81,6 +83,7 @@ pub const ALL_COLUMNS: AllColumns = (
8183
crates::homepage,
8284
crates::documentation,
8385
crates::readme,
86+
crates::readme_file,
8487
crates::license,
8588
crates::repository,
8689
crates::max_upload_size,
@@ -132,6 +135,7 @@ pub struct NewCrate<'a> {
132135
pub homepage: Option<&'a str>,
133136
pub documentation: Option<&'a str>,
134137
pub readme: Option<&'a str>,
138+
pub readme_file: Option<&'a str>,
135139
pub repository: Option<&'a str>,
136140
pub max_upload_size: Option<i32>,
137141
pub license: Option<&'a str>,

src/krate/publish.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ pub fn publish(req: &mut Request) -> CargoResult<Response> {
6666
homepage: new_crate.homepage.as_ref().map(|s| &**s),
6767
documentation: new_crate.documentation.as_ref().map(|s| &**s),
6868
readme: new_crate.readme.as_ref().map(|s| &**s),
69+
readme_file: new_crate.readme_file.as_ref().map(|s| &**s),
6970
repository: new_crate.repository.as_ref().map(|s| &**s),
7071
license: new_crate.license.as_ref().map(|s| &**s),
7172
max_upload_size: None,
@@ -124,7 +125,10 @@ pub fn publish(req: &mut Request) -> CargoResult<Response> {
124125

125126
// Render the README for this crate
126127
let readme = match new_crate.readme.as_ref() {
127-
Some(readme) => Some(render::markdown_to_html(&**readme)?),
128+
Some(readme) => Some(render::readme_to_html(
129+
&**readme,
130+
new_crate.readme_file.as_ref().map_or("README.md", |s| &**s),
131+
)?),
128132
None => None,
129133
};
130134

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ extern crate dotenv;
2222
extern crate flate2;
2323
extern crate git2;
2424
extern crate hex;
25+
extern crate htmlescape;
2526
extern crate lettre;
2627
extern crate license_exprs;
2728
#[macro_use]

src/render.rs

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
use ammonia::Builder;
22
use comrak;
3+
use htmlescape::encode_minimal;
34

45
use util::CargoResult;
56

67
/// Context for markdown to HTML rendering.
78
#[allow(missing_debug_implementations)]
8-
pub struct MarkdownRenderer<'a> {
9+
struct MarkdownRenderer<'a> {
910
html_sanitizer: Builder<'a>,
1011
}
1112

1213
impl<'a> MarkdownRenderer<'a> {
1314
/// Creates a new renderer instance.
14-
pub fn new() -> MarkdownRenderer<'a> {
15+
fn new() -> MarkdownRenderer<'a> {
1516
let tags = [
1617
"a",
1718
"b",
@@ -106,7 +107,7 @@ impl<'a> MarkdownRenderer<'a> {
106107
}
107108

108109
/// Renders the given markdown to HTML using the current settings.
109-
pub fn to_html(&self, text: &str) -> CargoResult<String> {
110+
fn to_html(&self, text: &str) -> CargoResult<String> {
110111
let options = comrak::ComrakOptions {
111112
ext_autolink: true,
112113
ext_strikethrough: true,
@@ -127,22 +128,46 @@ impl<'a> Default for MarkdownRenderer<'a> {
127128
}
128129
}
129130

130-
/// Renders a markdown text to sanitized HTML.
131+
/// Renders Markdown text to sanitized HTML.
132+
fn markdown_to_html(text: &str) -> CargoResult<String> {
133+
let renderer = MarkdownRenderer::new();
134+
renderer.to_html(text)
135+
}
136+
137+
/// Any readme with a filename ending in one of these extensions will be rendered as Markdown.
138+
/// Note we also render a readme as Markdown if _no_ extension is on the filename.
139+
static MARKDOWN_EXTENSIONS: [&'static str; 7] = [
140+
".md",
141+
".markdown",
142+
".mdown",
143+
".mdwn",
144+
".mkd",
145+
".mkdn",
146+
".mkdown",
147+
];
148+
149+
/// Renders a readme to sanitized HTML. An appropriate rendering method is chosen depending
150+
/// on the extension of the supplied filename.
131151
///
132152
/// The returned text should not contain any harmful HTML tag or attribute (such as iframe,
133153
/// onclick, onmouseover, etc.).
134154
///
135155
/// # Examples
136156
///
137157
/// ```
138-
/// use render::markdown_to_html;
158+
/// use render::render_to_html;
139159
///
140160
/// let text = "[Rust](https://rust-lang.org/) is an awesome *systems programming* language!";
141-
/// let rendered = markdown_to_html(text)?;
161+
/// let rendered = readme_to_html(text, "README.md")?;
142162
/// ```
143-
pub fn markdown_to_html(text: &str) -> CargoResult<String> {
144-
let renderer = MarkdownRenderer::new();
145-
renderer.to_html(text)
163+
pub fn readme_to_html(text: &str, filename: &str) -> CargoResult<String> {
164+
let filename = filename.to_lowercase();
165+
166+
if !filename.contains('.') || MARKDOWN_EXTENSIONS.iter().any(|e| filename.ends_with(e)) {
167+
return markdown_to_html(text);
168+
}
169+
170+
Ok(encode_minimal(text).replace("\n", "<br>\n"))
146171
}
147172

148173
#[cfg(test)]
@@ -218,6 +243,26 @@ mod tests {
218243
assert_eq!(result, "<p>Hello World!</p>\n");
219244
}
220245

246+
#[test]
247+
fn readme_to_html_renders_markdown() {
248+
for f in &["README", "readme.md", "README.MARKDOWN", "whatever.mkd"] {
249+
assert_eq!(
250+
readme_to_html("*lobster*", f).unwrap(),
251+
"<p><em>lobster</em></p>\n"
252+
);
253+
}
254+
}
255+
256+
#[test]
257+
fn readme_to_html_renders_other_things() {
258+
for f in &["readme.exe", "readem.org", "blah.adoc"] {
259+
assert_eq!(
260+
readme_to_html("<script>lobster</script>\n\nis my friend\n", f).unwrap(),
261+
"&lt;script&gt;lobster&lt;/script&gt;<br>\n<br>\nis my friend<br>\n"
262+
);
263+
}
264+
}
265+
221266
#[test]
222267
fn header_has_tags() {
223268
let text = "# My crate\n\nHello, world!\n";

src/schema.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,12 @@ table! {
282282
///
283283
/// (Automatically generated by Diesel.)
284284
readme -> Nullable<Varchar>,
285+
/// The `readme_file` column of the `crates` table.
286+
///
287+
/// Its SQL type is `Nullable<Varchar>`.
288+
///
289+
/// (Automatically generated by Diesel.)
290+
readme_file -> Nullable<Varchar>,
285291
/// The `textsearchable_index_col` column of the `crates` table.
286292
///
287293
/// Its SQL type is `Tsvector`.

src/tests/all.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ fn krate(name: &str) -> Crate {
480480
homepage: None,
481481
description: None,
482482
readme: None,
483+
readme_file: None,
483484
license: None,
484485
repository: None,
485486
max_upload_size: None,
@@ -647,6 +648,7 @@ fn new_req_body(
647648
homepage: krate.homepage,
648649
documentation: krate.documentation,
649650
readme: krate.readme,
651+
readme_file: krate.readme_file,
650652
keywords: Some(u::KeywordList(kws)),
651653
categories: Some(u::CategoryList(cats)),
652654
license: Some("MIT".to_string()),

src/tests/krate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ fn new_crate(name: &str) -> u::NewCrate {
7575
homepage: None,
7676
documentation: None,
7777
readme: None,
78+
readme_file: None,
7879
keywords: None,
7980
categories: None,
8081
license: Some("MIT".to_string()),

src/upload.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub struct NewCrate {
2323
pub homepage: Option<String>,
2424
pub documentation: Option<String>,
2525
pub readme: Option<String>,
26+
pub readme_file: Option<String>,
2627
pub keywords: Option<KeywordList>,
2728
pub categories: Option<CategoryList>,
2829
pub license: Option<String>,

0 commit comments

Comments
 (0)