Skip to content

Commit f0472c9

Browse files
Copy local image into rustdoc folder
1 parent 320ada6 commit f0472c9

File tree

4 files changed

+145
-17
lines changed

4 files changed

+145
-17
lines changed

src/librustdoc/externalfiles.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,24 @@ impl ExternalHtml {
3030
edition: Edition,
3131
playground: &Option<Playground>,
3232
) -> Option<ExternalHtml> {
33+
let mut images_to_copy = Vec::new();
3334
let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
3435
let ih = load_external_files(in_header, diag)?;
3536
let bc = load_external_files(before_content, diag)?;
3637
let m_bc = load_external_files(md_before_content, diag)?;
3738
let bc = format!(
3839
"{}{}",
3940
bc,
40-
Markdown(&m_bc, &[], id_map, codes, edition, playground).to_string()
41+
Markdown(&m_bc, &[], id_map, codes, edition, playground, &mut images_to_copy, &None)
42+
.to_string()
4143
);
4244
let ac = load_external_files(after_content, diag)?;
4345
let m_ac = load_external_files(md_after_content, diag)?;
4446
let ac = format!(
4547
"{}{}",
4648
ac,
47-
Markdown(&m_ac, &[], id_map, codes, edition, playground).to_string()
49+
Markdown(&m_ac, &[], id_map, codes, edition, playground, &mut images_to_copy, &None)
50+
.to_string()
4851
);
4952
Some(ExternalHtml { in_header: ih, before_content: bc, after_content: ac })
5053
}

src/librustdoc/html/markdown.rs

Lines changed: 87 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@ use rustc_data_structures::fx::FxHashMap;
2323
use rustc_span::edition::Edition;
2424
use std::borrow::Cow;
2525
use std::cell::RefCell;
26+
use std::collections::hash_map::DefaultHasher;
2627
use std::collections::VecDeque;
2728
use std::default::Default;
2829
use std::fmt::Write;
30+
use std::hash::{Hash, Hasher};
2931
use std::ops::Range;
32+
use std::path::{Path, PathBuf};
3033
use std::str;
3134

3235
use crate::html::highlight;
@@ -44,25 +47,33 @@ fn opts() -> Options {
4447

4548
/// When `to_string` is called, this struct will emit the HTML corresponding to
4649
/// the rendered version of the contained markdown string.
47-
pub struct Markdown<'a>(
50+
pub struct Markdown<'a, 'b>(
4851
pub &'a str,
4952
/// A list of link replacements.
5053
pub &'a [(String, String)],
5154
/// The current list of used header IDs.
5255
pub &'a mut IdMap,
5356
/// Whether to allow the use of explicit error codes in doctest lang strings.
5457
pub ErrorCodes,
55-
/// Default edition to use when parsing doctests (to add a `fn main`).
58+
/// Default edition to use when parsing dcotests (to add a `fn main`).
5659
pub Edition,
5760
pub &'a Option<Playground>,
61+
/// images_to_copy
62+
pub &'b mut Vec<(String, PathBuf)>,
63+
/// static_root_path
64+
pub &'b Option<String>,
5865
);
5966
/// A tuple struct like `Markdown` that renders the markdown with a table of contents.
60-
pub struct MarkdownWithToc<'a>(
67+
pub struct MarkdownWithToc<'a, 'b>(
6168
pub &'a str,
6269
pub &'a mut IdMap,
6370
pub ErrorCodes,
6471
pub Edition,
6572
pub &'a Option<Playground>,
73+
/// images_to_copy
74+
pub &'b mut Vec<(String, PathBuf)>,
75+
/// static_root_path
76+
pub &'b Option<String>,
6677
);
6778
/// A tuple struct like `Markdown` that renders the markdown escaping HTML tags.
6879
pub struct MarkdownHtml<'a>(
@@ -550,6 +561,56 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for Footnotes<'a, I> {
550561
}
551562
}
552563

564+
struct LocalImages<'a, 'b, I: Iterator<Item = Event<'a>>> {
565+
inner: I,
566+
images_to_copy: &'b mut Vec<(String, PathBuf)>,
567+
static_root_path: &'b Option<String>,
568+
}
569+
570+
impl<'a, 'b, I: Iterator<Item = Event<'a>>> LocalImages<'a, 'b, I> {
571+
fn new(
572+
iter: I,
573+
images_to_copy: &'b mut Vec<(String, PathBuf)>,
574+
static_root_path: &'b Option<String>,
575+
) -> Self {
576+
LocalImages { inner: iter, images_to_copy, static_root_path }
577+
}
578+
}
579+
580+
impl<'a, 'b, I: Iterator<Item = Event<'a>>> Iterator for LocalImages<'a, 'b, I> {
581+
type Item = Event<'a>;
582+
583+
fn next(&mut self) -> Option<Self::Item> {
584+
let event = self.inner.next();
585+
if let Some(Event::Start(Tag::Image(type_, ref url, ref title))) = event {
586+
if url.starts_with("http://") || url.starts_with("https://") {
587+
// Not a local image, move on!
588+
}
589+
if let Ok(url) = Path::new(&url.clone().into_string()).canonicalize() {
590+
let mut hasher = DefaultHasher::new();
591+
url.hash(&mut hasher);
592+
let hash = format!("{:x}", hasher.finish());
593+
let static_folder_path = format!("static/{}", hash);
594+
if self.images_to_copy.iter().find(|(h, _)| *h == hash).is_none() {
595+
self.images_to_copy.push((hash, url));
596+
}
597+
return Some(match self.static_root_path {
598+
Some(p) => {
599+
let s = format!("../{}", Path::new(p).join(&static_folder_path).display());
600+
Event::Start(Tag::Image(type_, CowStr::Boxed(s.into()), title.clone()))
601+
}
602+
None => Event::Start(Tag::Image(
603+
type_,
604+
CowStr::Boxed(format!("../{}", static_folder_path).into()),
605+
title.clone(),
606+
)),
607+
});
608+
}
609+
}
610+
event
611+
}
612+
}
613+
553614
pub fn find_testable_code<T: test::Tester>(
554615
doc: &str,
555616
tests: &mut T,
@@ -720,9 +781,18 @@ impl LangString {
720781
}
721782
}
722783

723-
impl Markdown<'_> {
784+
impl Markdown<'_, '_> {
724785
pub fn to_string(self) -> String {
725-
let Markdown(md, links, mut ids, codes, edition, playground) = self;
786+
let Markdown(
787+
md,
788+
links,
789+
mut ids,
790+
codes,
791+
edition,
792+
playground,
793+
images_to_copy,
794+
static_root_path,
795+
) = self;
726796

727797
// This is actually common enough to special-case
728798
if md.is_empty() {
@@ -742,6 +812,7 @@ impl Markdown<'_> {
742812

743813
let p = HeadingLinks::new(p, None, &mut ids);
744814
let p = LinkReplacer::new(p, links);
815+
let p = LocalImages::new(p, images_to_copy, static_root_path);
745816
let p = CodeBlocks::new(p, codes, edition, playground);
746817
let p = Footnotes::new(p);
747818
html::push_html(&mut s, p);
@@ -750,9 +821,17 @@ impl Markdown<'_> {
750821
}
751822
}
752823

753-
impl MarkdownWithToc<'_> {
824+
impl MarkdownWithToc<'_, '_> {
754825
pub fn to_string(self) -> String {
755-
let MarkdownWithToc(md, mut ids, codes, edition, playground) = self;
826+
let MarkdownWithToc(
827+
md,
828+
mut ids,
829+
codes,
830+
edition,
831+
playground,
832+
images_to_copy,
833+
static_root_path,
834+
) = self;
756835

757836
let p = Parser::new_ext(md, opts());
758837

@@ -762,6 +841,7 @@ impl MarkdownWithToc<'_> {
762841

763842
{
764843
let p = HeadingLinks::new(p, Some(&mut toc), &mut ids);
844+
let p = LocalImages::new(p, images_to_copy, static_root_path);
765845
let p = CodeBlocks::new(p, codes, edition, playground);
766846
let p = Footnotes::new(p);
767847
html::push_html(&mut s, p);

src/librustdoc/html/render.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ use crate::html::item_type::ItemType;
7272
use crate::html::markdown::{self, ErrorCodes, IdMap, Markdown, MarkdownHtml, MarkdownSummaryLine};
7373
use crate::html::sources;
7474
use crate::html::{highlight, layout, static_files};
75+
use crate::markdown::generate_static_images;
7576

7677
use minifier;
7778

@@ -203,6 +204,10 @@ crate struct SharedContext {
203204
pub edition: Edition,
204205
pub codes: ErrorCodes,
205206
playground: Option<markdown::Playground>,
207+
/// Local images to move into the static folder.
208+
///
209+
/// The tuple contains the hash as first argument and the image original path.
210+
pub images_to_copy: RefCell<Vec<(String, PathBuf)>>,
206211
}
207212

208213
impl Context {
@@ -482,9 +487,10 @@ pub fn run(
482487
edition,
483488
codes: ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()),
484489
playground,
490+
images_to_copy: RefCell::new(Vec::new()),
485491
};
486492

487-
let dst = output;
493+
let dst = output.clone();
488494
scx.ensure_dir(&dst)?;
489495
krate = sources::render(&dst, &mut scx, krate)?;
490496
let (new_crate, index, cache) =
@@ -1349,6 +1355,8 @@ impl Context {
13491355
);
13501356
self.shared.fs.write(&settings_file, v.as_bytes())?;
13511357

1358+
generate_static_images(&self.dst, &*self.shared.images_to_copy.borrow());
1359+
13521360
Ok(())
13531361
}
13541362

@@ -1801,6 +1809,7 @@ fn render_markdown(
18011809
is_hidden: bool,
18021810
) {
18031811
let mut ids = cx.id_map.borrow_mut();
1812+
let mut images_to_copy = cx.shared.images_to_copy.borrow_mut();
18041813
write!(
18051814
w,
18061815
"<div class='docblock{}'>{}{}</div>",
@@ -1812,7 +1821,9 @@ fn render_markdown(
18121821
&mut ids,
18131822
cx.shared.codes,
18141823
cx.shared.edition,
1815-
&cx.shared.playground
1824+
&cx.shared.playground,
1825+
&mut images_to_copy,
1826+
&cx.shared.static_root_path,
18161827
)
18171828
.to_string()
18181829
)
@@ -3660,6 +3671,7 @@ fn render_impl(
36603671
write!(w, "</h3>");
36613672
if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) {
36623673
let mut ids = cx.id_map.borrow_mut();
3674+
let mut images_to_copy = cx.shared.images_to_copy.borrow_mut();
36633675
write!(
36643676
w,
36653677
"<div class='docblock'>{}</div>",
@@ -3669,7 +3681,9 @@ fn render_impl(
36693681
&mut ids,
36703682
cx.shared.codes,
36713683
cx.shared.edition,
3672-
&cx.shared.playground
3684+
&cx.shared.playground,
3685+
&mut images_to_copy,
3686+
&cx.shared.static_root_path,
36733687
)
36743688
.to_string()
36753689
);

src/librustdoc/markdown.rs

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use std::fs::File;
1+
use std::fs::{self, File};
22
use std::io::prelude::*;
3-
use std::path::PathBuf;
3+
use std::path::{Path, PathBuf};
44

55
use rustc_feature::UnstableFeatures;
66
use rustc_span::edition::Edition;
@@ -76,10 +76,21 @@ pub fn render(
7676

7777
let mut ids = IdMap::new();
7878
let error_codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
79+
let mut images_to_copy = Vec::new();
7980
let text = if !options.markdown_no_toc {
80-
MarkdownWithToc(text, &mut ids, error_codes, edition, &playground).to_string()
81+
MarkdownWithToc(
82+
text,
83+
&mut ids,
84+
error_codes,
85+
edition,
86+
&playground,
87+
&mut images_to_copy,
88+
&None,
89+
)
90+
.to_string()
8191
} else {
82-
Markdown(text, &[], &mut ids, error_codes, edition, &playground).to_string()
92+
Markdown(text, &[], &mut ids, error_codes, edition, &playground, &mut images_to_copy, &None)
93+
.to_string()
8394
};
8495

8596
let err = write!(
@@ -122,7 +133,27 @@ pub fn render(
122133
diag.struct_err(&format!("cannot write to `{}`: {}", output.display(), e)).emit();
123134
6
124135
}
125-
Ok(_) => 0,
136+
Ok(_) => {
137+
generate_static_images(&output, &images_to_copy);
138+
0
139+
}
140+
}
141+
}
142+
143+
pub fn generate_static_images<P: AsRef<Path>>(target_dir: P, images_to_copy: &[(String, PathBuf)]) {
144+
if images_to_copy.is_empty() {
145+
return;
146+
}
147+
let target_dir = target_dir.as_ref().join("static");
148+
let _ = fs::create_dir(&target_dir);
149+
for (hash, image_to_copy) in images_to_copy {
150+
if fs::copy(image_to_copy, target_dir.join(hash)).is_err() {
151+
eprintln!(
152+
"Couldn't copy `{}` into `{}`...",
153+
image_to_copy.display(),
154+
target_dir.display()
155+
);
156+
}
126157
}
127158
}
128159

0 commit comments

Comments
 (0)