diff --git a/build.rs b/build.rs index 907df3ff0..30c3ca369 100644 --- a/build.rs +++ b/build.rs @@ -11,6 +11,14 @@ fn main() { env::var("TARGET").unwrap() ); + // Don't rerun anytime a single change is made + println!("cargo:rerun-if-changed=templates/style.scss"); + println!("cargo:rerun-if-changed=templates/menu.js"); + println!("cargo:rerun-if-changed=templates/index.js"); + // TODO: are these right? + println!("cargo:rerun-if-changed=.git/HEAD"); + println!("cargo:rerun-if-changed=.git/index"); + write_git_version(); compile_sass(); copy_js(); diff --git a/src/web/page/mod.rs b/src/web/page/mod.rs index b8a42458e..d6d68c1d4 100644 --- a/src/web/page/mod.rs +++ b/src/web/page/mod.rs @@ -2,7 +2,7 @@ mod templates; mod web_page; pub(crate) use templates::TemplateData; -pub(crate) use web_page::WebPage; +pub(super) use web_page::WebPage; use serde::Serialize; diff --git a/src/web/page/web_page.rs b/src/web/page/web_page.rs index 5c38b76e5..a4bb92937 100644 --- a/src/web/page/web_page.rs +++ b/src/web/page/web_page.rs @@ -1,14 +1,21 @@ use super::TemplateData; use iron::{headers::ContentType, response::Response, status::Status, IronResult, Request}; use serde::Serialize; +use std::borrow::Cow; use tera::Context; /// When making using a custom status, use a closure that coerces to a `fn(&Self) -> Status` #[macro_export] macro_rules! impl_webpage { + ($page:ty = $template:literal $(, status = $status:expr)? $(, content_type = $content_type:expr)? $(,)?) => { + impl_webpage!($page = |_| ::std::borrow::Cow::Borrowed($template) $(, status = $status)? $(, content_type = $content_type)?); + }; ($page:ty = $template:expr $(, status = $status:expr)? $(, content_type = $content_type:expr)? $(,)?) => { impl $crate::web::page::WebPage for $page { - const TEMPLATE: &'static str = $template; + fn template(&self) -> ::std::borrow::Cow<'static, str> { + let template: fn(&Self) -> ::std::borrow::Cow<'static, str> = $template; + template(self) + } $( fn get_status(&self) -> ::iron::status::Status { @@ -32,14 +39,13 @@ pub trait WebPage: Serialize + Sized { // TODO: We could cache similar pages using the `&Context` fn into_response(self, req: &Request) -> IronResult { let ctx = Context::from_serialize(&self).unwrap(); - let rendered = req .extensions .get::() .expect("missing TemplateData from the request extensions") .templates .load() - .render(Self::TEMPLATE, &ctx) + .render(&self.template(), &ctx) .unwrap(); let mut response = Response::with((self.get_status(), rendered)); @@ -49,7 +55,7 @@ pub trait WebPage: Serialize + Sized { } /// The name of the template to be rendered - const TEMPLATE: &'static str; + fn template(&self) -> Cow<'static, str>; /// Gets the status of the request, defaults to `Ok` fn get_status(&self) -> Status { diff --git a/src/web/routes.rs b/src/web/routes.rs index a13dff4f6..a7a8d5d21 100644 --- a/src/web/routes.rs +++ b/src/web/routes.rs @@ -23,6 +23,8 @@ pub(super) fn build_routes() -> Routes { routes.internal_page("/about", super::sitemap::about_handler); routes.internal_page("/about/metrics", super::metrics::metrics_handler); + routes.internal_page("/about/builds", super::sitemap::about_builds_handler); + routes.internal_page("/about/:subpage", super::sitemap::about_handler); routes.internal_page("/releases", super::releases::recent_releases_handler); routes.static_resource("/releases/feed", super::releases::releases_feed_handler); diff --git a/src/web/sitemap.rs b/src/web/sitemap.rs index c1944554d..46a751463 100644 --- a/src/web/sitemap.rs +++ b/src/web/sitemap.rs @@ -56,16 +56,18 @@ pub fn robots_txt_handler(_: &mut Request) -> IronResult { } #[derive(Debug, Clone, PartialEq, Eq, Serialize)] -struct About { +struct AboutBuilds { /// The current version of rustc that docs.rs is using to build crates rustc_version: Option, /// The default crate build limits limits: Limits, + /// Just for the template, since this isn't shared with AboutPage + active_tab: &'static str, } -impl_webpage!(About = "core/about.html"); +impl_webpage!(AboutBuilds = "core/about/builds.html"); -pub fn about_handler(req: &mut Request) -> IronResult { +pub fn about_builds_handler(req: &mut Request) -> IronResult { let mut conn = extension!(req, Pool).get()?; let res = ctry!( req, @@ -80,9 +82,45 @@ pub fn about_handler(req: &mut Request) -> IronResult { } }); - About { + AboutBuilds { rustc_version, limits: Limits::default(), + active_tab: "builds", + } + .into_response(req) +} + +#[derive(Serialize)] +struct AboutPage<'a> { + #[serde(skip)] + template: String, + active_tab: &'a str, +} + +impl_webpage!(AboutPage<'_> = |this: &AboutPage| this.template.clone().into()); + +pub fn about_handler(req: &mut Request) -> IronResult { + use super::ErrorPage; + use iron::status::Status; + + let name = match *req.url.path().last().expect("iron is broken") { + "about" | "index" => "index", + x @ "badges" | x @ "metadata" | x @ "redirections" => x, + _ => { + let msg = "This /about page does not exist. \ + Perhaps you are interested in creating it?"; + let page = ErrorPage { + title: "The requested page does not exist", + message: Some(msg.into()), + status: Status::NotFound, + }; + return page.into_response(req); + } + }; + let template = format!("core/about/{}.html", name); + AboutPage { + template, + active_tab: name, } .into_response(req) } @@ -110,6 +148,19 @@ mod tests { fn about_page() { wrapper(|env| { let web = env.frontend(); + for file in std::fs::read_dir("templates/core/about")? { + use std::ffi::OsStr; + + let file_path = file?.path(); + if file_path.extension() != Some(OsStr::new("html")) + || file_path.file_stem() == Some(OsStr::new("index")) + { + continue; + } + let filename = file_path.file_stem().unwrap().to_str().unwrap(); + let path = format!("/about/{}", filename); + assert_success(&path, web)?; + } assert_success("/about", web) }) } diff --git a/templates/about-base.html b/templates/about-base.html new file mode 100644 index 000000000..1ca789783 --- /dev/null +++ b/templates/about-base.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} + +{% block header %} +
+
+

Docs.rs documentation

+
+
    + {% set text = ' About' %} + {{ macros::active_link(expected="index", href="/about", text=text) }} + {% set text = ' Badges' %} + {{ macros::active_link(expected="badges", href="/about/badges", text=text) }} + {% set text = ' Builds' %} + {{ macros::active_link(expected="builds", href="/about/builds", text=text) }} + {% set text = ' Metadata' %} + {{ macros::active_link(expected="metadata", href="/about/metadata", text=text) }} + {% set text = ' Shorthand URLs' %} + {{ macros::active_link(expected="redirections", href="/about/redirections", text=text) }} +
+
+
+
+{% endblock %} diff --git a/templates/core/about.html b/templates/core/about.html deleted file mode 100644 index 490c96a2b..000000000 --- a/templates/core/about.html +++ /dev/null @@ -1,201 +0,0 @@ -{% extends "base.html" -%} - -{%- block title -%} About Docs.rs {%- endblock title -%} - -{%- block body -%} - {%- set docsrs_repo = "https://github.com/rust-lang/docs.rs" -%} - -

About Docs.rs

-
-

- Docs.rs (formerly cratesfyi) is an - open source - documentation host for crates of the - Rust Programming Language. -

- -

- Docs.rs automatically builds crates' documentation released on - crates.io - using the nightly release of the Rust compiler. - - {%- if rustc_version %} - The current version of the Rust compiler in use is {{ rustc_version }}. - Builds can take a while depending how many crates are in the queue. - If you need a newer version of this compiler, check the - issues page - and file a new issue if you don't see an existing request. - {%- endif -%} -

- -

- Docs.rs builds crates with environment variable DOCS_RS set to - 1, which enables the crate to detect docs.rs and build the - crate differently for docs. -

- -

- The source code of Docs.rs is available on - GitHub. If - you ever encounter an issue, don't hesitate to report it! (And - thanks in advance!) -

- -

- The README of a crate is taken from the readme field defined in - Cargo.toml. If a crate doesn't have this field, - no README will be displayed. -

- -

Global sandbox limits

- -

- All the builds on docs.rs are executed inside a sandbox with limited - resources. The current limits are the following: -

- - {{ macros::crate_limits(limits=limits) }} - -

- If a build fails because it hit one of those limits please - open an issue - to get them increased for your crate. -

- -

Redirections

- -

- Docs.rs uses semver to parse URLs. You can use this feature to access - crates' documentation easily. Example of URL redirections for - clap crate: -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
URLRedirects to documentation of
docs.rs/clapLatest version of clap
docs.rs/clap/~22.* version
docs.rs/clap/~2.92.9.* version
- docs.rs/clap/2.9.3 - - 2.9.3 version (you don't need = unlike semver) -
- docs.rs/clap/*/clap/struct.App.html - - Latest version of this page (if it still exists). "latest" or "newest" work as well as - *. -
- -

Badges

- -

- You can use badges to show state of your documentation to your users. - The default badge will be pointed at the latest version of a crate. - You can use version parameter to show status of documentation for - any version you want. -

- -

- Badge will display in blue if docs.rs is successfully hosting your crate - documentation, and red if building documentation failing. -

- -

Example badges for mio crate:

- - - - - - - - - - {%- set mio_badge = "https://docs.rs/mio/badge.svg" -%} - - - - - - - - - - - - - - - -
URLBadge
Latest version: {{ mio_badge }}mio
- Version 0.4.4: {{ mio_badge }}?version=0.4.4 - mio
- Version 0.1.0: {{ mio_badge }}?version=0.1.0 - mio
- -

Metadata for custom builds

- -

- You can customize docs.rs builds by defining [package.metadata.docs.rs] - table in your crates' Cargo.toml. -

- -

The available configuration flags you can customize are:

- -
{%- include "core/Cargo.toml.example" -%}
- -

Test crate documentation build locally

- {%- set build_subcommand = docsrs_repo ~ "/blob/master/README.md#build-subcommand" -%} -

- The docs.rs README describes how to build an - unpublished crate's documentation locally using the same build environment as the build agent. -

- -

Version

-

Currently running Docs.rs version is: {{ docsrs_version() }}

- -

Contact

- {%- set governance_link = "https://www.rust-lang.org/governance/teams/dev-tools#docs-rs" -%} -

- Docs.rs is run and maintained by the Docs.rs team. - You can find us in #docs-rs on Discord. -

- -
-{%- endblock body %} - -{% block css -%} - {{ macros::highlight_css() }} -{%- endblock css %} - -{% block javascript -%} - {{ macros::highlight_js(languages=["ini"]) }} -{%- endblock javascript %} diff --git a/templates/core/about/badges.html b/templates/core/about/badges.html new file mode 100644 index 000000000..f2f6d24f0 --- /dev/null +++ b/templates/core/about/badges.html @@ -0,0 +1,53 @@ +{% extends "about-base.html" -%} + +{%- block title -%} Badges {%- endblock title -%} + +{%- block body -%} +

Badges

+ +
+

+ You can use badges to show state of your documentation to your users. + The default badge will be pointed at the latest version of a crate. + You can use version parameter to show status of documentation for + any version you want. +

+ +

+ Badge will display in blue if docs.rs is successfully hosting your crate + documentation, and red if building documentation failing. +

+ +

Example badges for mio crate:

+ + + + + + + + + + {%- set mio_badge = "https://docs.rs/mio/badge.svg" -%} + + + + + + + + + + + + + + + +
URLBadge
Latest version: {{ mio_badge }}mio
+ Version 0.4.4: {{ mio_badge }}?version=0.4.4 + mio
+ Version 0.1.0: {{ mio_badge }}?version=0.1.0 + mio
+
+{%- endblock body %} diff --git a/templates/core/about/builds.html b/templates/core/about/builds.html new file mode 100644 index 000000000..41057fdfd --- /dev/null +++ b/templates/core/about/builds.html @@ -0,0 +1,85 @@ +{% extends "about-base.html" -%} + +{%- block title -%} Builds {%- endblock title -%} + +{%- block body -%} + {%- set docsrs_repo = "https://github.com/rust-lang/docs.rs" -%} +

Builds

+
+

+ Docs.rs automatically builds crates' documentation released on + crates.io + using the nightly release of the Rust compiler. + Builds can take a while depending how many crates are in the queue. +

+ +

+ {%- if rustc_version %} + The current version of the Rust compiler in use is {{ rustc_version }}. + {%- endif -%} +

+ +

+ The README of a crate is taken from the readme field defined in + Cargo.toml. If a crate doesn't have this field, + no README will be displayed. +

+ +

Diagnosing a failed build

+ +

Missing dependencies

+

+ Missing dependencies are a common reason for a failed build. + Docs.rs dependencies are managed through + crates-build-env; + see Forge for instructions + on how to make a PR. You can always file an issue + if you're having trouble. +

+ +

Detecting Docs.rs from build.rs

+

+ Docs.rs builds crates with the environment variable DOCS_RS set to + 1, which enables the crate to detect docs.rs and build the + crate differently. This can be helpful if you need + dependencies for building the library, but not for building the documentation. +

+ +

Detecting Docs.rs from #[cfg] attributes

+

+ You can detect Docs.rs by having a feature + which is only set by Docs.rs. See Metadata for more information. +

+ +

Global sandbox limits

+ +

+ All the builds on docs.rs are executed inside a sandbox with limited + resources. The current limits are the following: +

+ + {{ macros::crate_limits(limits=limits) }} + +

+ If a build fails because it hit one of those limits please + open an issue + to get them increased for your crate. + Note that network access will not be enabled for any crate. +

+ +

Test crate documentation build locally

+ {%- set build_subcommand = docsrs_repo ~ "/blob/master/README.md#build-subcommand" -%} +

+ The docs.rs README describes how to build an + unpublished crate's documentation locally using the same build environment as the build agent. +

+
+{%- endblock body %} + +{% block css -%} + {{ macros::highlight_css() }} +{%- endblock css %} + +{% block javascript -%} + {{ macros::highlight_js(languages=["ini"]) }} +{%- endblock javascript %} diff --git a/templates/core/about/index.html b/templates/core/about/index.html new file mode 100644 index 000000000..5d3074f1f --- /dev/null +++ b/templates/core/about/index.html @@ -0,0 +1,47 @@ +{% extends "about-base.html" -%} + +{%- block title -%} About Docs.rs {%- endblock title -%} + +{%- block body -%} + {%- set docsrs_repo = "https://github.com/rust-lang/docs.rs" -%} + +

About Docs.rs

+
+

+ Docs.rs is an + open source + documentation host for crates of the + Rust Programming Language. + All libraries published to crates.io + are documented. If you just published a crate, your crate is likely + still in the queue. +

+ +

+ The source code of Docs.rs is available on + GitHub. If + you ever encounter an issue, don't hesitate to report it! (And + thanks in advance!) +

+ + +

More about Docs.rs

+
    +
  1. Badges: How to use badges generated by Docs.rs
  2. +
  3. Builds: How Docs.rs builds documentation for a crate
  4. +
  5. Metadata: How you can configure a build
  6. +
  7. Redirections: How Docs.rs uses semantic versioning in URLs
  8. +
+ +

Version

+

Currently running Docs.rs version is: {{ docsrs_version() }}

+ +

Contact

+ {%- set governance_link = "https://www.rust-lang.org/governance/teams/dev-tools#docs-rs" -%} +

+ Docs.rs is run and maintained by the Docs.rs team. + You can find us in #docs-rs on Discord. +

+ +
+{%- endblock body %} diff --git a/templates/core/about/metadata.html b/templates/core/about/metadata.html new file mode 100644 index 000000000..d2c0d7fc1 --- /dev/null +++ b/templates/core/about/metadata.html @@ -0,0 +1,18 @@ +{% extends "about-base.html" -%} + +{%- block title -%} Metadata {%- endblock title -%} + +{%- block body -%} +

Metadata for custom builds

+ +
+

+ You can customize docs.rs builds by defining [package.metadata.docs.rs] + table in your crates' Cargo.toml. +

+ +

The available configuration flags you can customize are:

+ +
{%- include "core/Cargo.toml.example" -%}
+
+{%- endblock body %} diff --git a/templates/core/about/redirections.html b/templates/core/about/redirections.html new file mode 100644 index 000000000..d157f706d --- /dev/null +++ b/templates/core/about/redirections.html @@ -0,0 +1,60 @@ +{% extends "about-base.html" -%} + +{%- block title -%} Shorthand URLs {%- endblock title -%} + +{%- block body -%} +

Shorthand URLs

+
+ +

+ Docs.rs uses semver to parse URLs. You can use this feature to access + crates' documentation easily. Example of URL redirections for + clap crate: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
URLRedirects to documentation of
docs.rs/clapLatest version of clap
docs.rs/clap/~22.* version
docs.rs/clap/~2.92.9.* version
+ docs.rs/clap/2.9.3 + + 2.9.3 version (you don't need = unlike semver) +
+ docs.rs/clap/*/clap/struct.App.html + + Latest version of this page (if it still exists). "latest" or "newest" work as well as + *. +
+
+{%- endblock body %} diff --git a/templates/crate/details.html b/templates/crate/details.html index 15a5e8c3f..61d581a72 100644 --- a/templates/crate/details.html +++ b/templates/crate/details.html @@ -147,9 +147,15 @@ {%- else -%} {# Display a warning telling the user we failed to build the docs #}
- docs.rs failed to build {{ details.name }}-{{ details.version }}
Please check the - build logs and, if you believe this is - docs.rs' fault, open an issue. + docs.rs failed to build {{ details.name }}-{{ details.version }} +
+ Please check the + build logs for more information. +
+ See Builds for ideas on how to fix a failed build, + or Metadata for how to configure docs.rs builds. +
+ If you believe this is docs.rs' fault, open an issue.
{# If there is one, display the most next most recent successful build #} diff --git a/templates/header/topbar.html b/templates/header/topbar.html index c1662f6a5..a58c15a77 100644 --- a/templates/header/topbar.html +++ b/templates/header/topbar.html @@ -24,6 +24,12 @@ diff --git a/templates/macros.html b/templates/macros.html index b51f26cdf..fb2ff5369 100644 --- a/templates/macros.html +++ b/templates/macros.html @@ -26,6 +26,21 @@ type="text/css" media="all" /> {% endmacro highlight_css %} +{# + Creates a list entry for active tabs. When the active tab is the same as `expected`, it will show the current tab as active. + * `expected` A string that represents the current tab, when `active_tab == expected` the current will be shown as active + * `href` A string used as the tab's link + * `text` A string used as the tab's text +#} +{% macro active_link(expected, href, text) %} +
  • + + {# safe: allow passing in HTML #} + {{ text | safe }} + +
  • +{% endmacro active_link %} + {# Creates a formatted table showing the resource limits of a crate * `limits` A non-null `Limits` struct