diff --git a/assets/images/icons.png b/assets/images/icons.png
index cc60919e..ab408fa9 100644
Binary files a/assets/images/icons.png and b/assets/images/icons.png differ
diff --git a/assets/images/icons@2x.png b/assets/images/icons@2x.png
index c15a731c..1376275a 100644
Binary files a/assets/images/icons@2x.png and b/assets/images/icons@2x.png differ
diff --git a/assets/javascripts/collections/types.coffee b/assets/javascripts/collections/types.coffee
index 8edf1f4d..0b0a5b3d 100644
--- a/assets/javascripts/collections/types.coffee
+++ b/assets/javascripts/collections/types.coffee
@@ -7,7 +7,7 @@ class app.collections.Types extends app.Collection
(result[@_groupFor(type)] ||= []).push(type)
result.filter (e) -> e.length > 0
- GUIDES_RGX = /guide|tutorial|getting\ started/i
+ GUIDES_RGX = /guide|tutorial|reference|getting\ started/i
_groupFor: (type) ->
if GUIDES_RGX.test(type.name)
diff --git a/assets/javascripts/news.json b/assets/javascripts/news.json
index 8592da88..171af38d 100644
--- a/assets/javascripts/news.json
+++ b/assets/javascripts/news.json
@@ -1,5 +1,8 @@
[
[
+ "2015-05-24",
+ "New Rust documentation"
+ ], [
"2015-04-26",
"New Apache HTTP Server and npm documentations"
], [
diff --git a/assets/javascripts/views/pages/rust.coffee b/assets/javascripts/views/pages/rust.coffee
new file mode 100644
index 00000000..979d3990
--- /dev/null
+++ b/assets/javascripts/views/pages/rust.coffee
@@ -0,0 +1,6 @@
+#= require views/pages/base
+
+class app.views.RustPage extends app.views.BasePage
+ afterRender: ->
+ @highlightCode @findAll('pre.rust'), 'rust'
+ return
diff --git a/assets/stylesheets/application-dark.css.scss b/assets/stylesheets/application-dark.css.scss
index d4ae5fc3..491ed4dc 100644
--- a/assets/stylesheets/application-dark.css.scss
+++ b/assets/stylesheets/application-dark.css.scss
@@ -62,6 +62,7 @@
'pages/requirejs',
'pages/rethinkdb',
'pages/rfc',
+ 'pages/rust',
'pages/socketio',
'pages/sphinx',
'pages/underscore',
diff --git a/assets/stylesheets/application.css.scss b/assets/stylesheets/application.css.scss
index 05328ddc..8589a72a 100644
--- a/assets/stylesheets/application.css.scss
+++ b/assets/stylesheets/application.css.scss
@@ -62,6 +62,7 @@
'pages/requirejs',
'pages/rethinkdb',
'pages/rfc',
+ 'pages/rust',
'pages/socketio',
'pages/sphinx',
'pages/underscore',
diff --git a/assets/stylesheets/global/_icons.scss b/assets/stylesheets/global/_icons.scss
index 0c968c46..01ab7214 100644
--- a/assets/stylesheets/global/_icons.scss
+++ b/assets/stylesheets/global/_icons.scss
@@ -65,7 +65,7 @@
._icon-go:before { background-position: -1rem -4rem; }
._icon-express:before { background-position: -2rem -4rem; }
._icon-grunt:before { background-position: -3rem -4rem; }
-
+._icon-rust:before { background-position: -4rem -4rem; @extend %darkIconFix !optional; }
._icon-laravel:before { background-position: -5rem -4rem; }
._icon-haskell:before { background-position: -6rem -4rem; }
._icon-requirejs:before { background-position: -7rem -4rem; }
diff --git a/assets/stylesheets/pages/_rust.scss b/assets/stylesheets/pages/_rust.scss
new file mode 100644
index 00000000..200415df
--- /dev/null
+++ b/assets/stylesheets/pages/_rust.scss
@@ -0,0 +1,10 @@
+._rust {
+ @extend %simple;
+
+ h4 { @extend %block-label; }
+ .docblock { margin-left: 1em; }
+
+ div.stability { margin-bottom: 1em; }
+ em.stab, span.stab { @extend %label; }
+ em.stab.unstable, span.stab.unstable { @extend %label-orange; }
+}
diff --git a/lib/docs/filters/rust/clean_html.rb b/lib/docs/filters/rust/clean_html.rb
new file mode 100644
index 00000000..f309a76b
--- /dev/null
+++ b/lib/docs/filters/rust/clean_html.rb
@@ -0,0 +1,63 @@
+module Docs
+ class Rust
+ class CleanHtmlFilter < Filter
+ def call
+ if slug.start_with?('book')
+ book
+ elsif slug.start_with?('reference')
+ reference
+ else
+ api
+ end
+
+ css('.rusttest', 'hr').remove
+
+ css('.docblock > h1').each { |node| node.name = 'h4' }
+ css('h2.section-header').each { |node| node.name = 'h3' }
+ css('h1.section-header').each { |node| node.name = 'h2' }
+
+ css('> .impl-items', '> .docblock').each do |node|
+ node.before(node.children).remove
+ end
+
+ css('h1 > a', 'h2 > a', 'h3 > a', 'h4 > a', 'h5 > a').each do |node|
+ node.before(node.children).remove
+ end
+
+ css('pre > code').each do |node|
+ node.parent['class'] = node['class']
+ node.before(node.children).remove
+ end
+
+ css('pre').each do |node|
+ node.content = node.content
+ end
+
+ doc
+ end
+
+ def book
+ @doc = at_css('#page')
+ end
+
+ def reference
+ css('#versioninfo').remove
+ end
+
+ def api
+ @doc = at_css('#main')
+
+ css('.toggle-wrapper').remove
+
+ css('h1.fqn').each do |node|
+ node.content = node.at_css('.in-band').content
+ end
+
+ css('.stability .stab').each do |node|
+ node.name = 'span'
+ node.content = node.content
+ end
+ end
+ end
+ end
+end
diff --git a/lib/docs/filters/rust/entries.rb b/lib/docs/filters/rust/entries.rb
new file mode 100644
index 00000000..c5c8f324
--- /dev/null
+++ b/lib/docs/filters/rust/entries.rb
@@ -0,0 +1,56 @@
+module Docs
+ class Rust
+ class EntriesFilter < Docs::EntriesFilter
+ def get_name
+ if slug.start_with?('book')
+ at_css("#toc a[href='#{File.basename(slug)}']").content
+ elsif slug.start_with?('reference')
+ 'Reference'
+ else
+ name = at_css('h1.fqn .in-band').content.remove(/\A.+\s/)
+ mod = slug.split('/').first
+ name.prepend("#{mod}::") unless name.start_with?(mod)
+ name
+ end
+ end
+
+ PRIMITIVE_SLUG = /\A(\w+)\/(primitive)\./
+
+ def get_type
+ if slug.start_with?('book')
+ 'Guide'
+ elsif slug.start_with?('reference')
+ 'Reference'
+ else
+ path = name.split('::')
+ heading = at_css('h1.fqn .in-band').content.strip
+ if path.length > 2 || (path.length == 2 && (heading.start_with?('Module') || heading.start_with?('Primitive')))
+ path[0..1].join('::')
+ else
+ path[0]
+ end
+ end
+ end
+
+ def additional_entries
+ if slug.start_with?('book')
+ []
+ elsif slug.start_with?('reference')
+ css('#TOC > ul > li > a', '#TOC > ul > li > ul > li > a').map do |node|
+ name = node.content
+ name.sub! %r{(\d)\ }, '\1. '
+ name.sub! '10.0.', '10.'
+ id = node['href'].remove('#')
+ [name, id]
+ end
+ else
+ css('#methods + * + div > .method', '#required-methods + div > .method', '#provided-methods + div > .method').map do |node|
+ name = node.at_css('.fnname').content
+ name.prepend "#{self.name}::"
+ [name, node['id']]
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/docs/scrapers/rust.rb b/lib/docs/scrapers/rust.rb
new file mode 100644
index 00000000..1a36dd39
--- /dev/null
+++ b/lib/docs/scrapers/rust.rb
@@ -0,0 +1,48 @@
+module Docs
+ class Rust < UrlScraper
+ self.type = 'rust'
+ self.version = '1.0.0'
+ self.base_url = 'http://doc.rust-lang.org/'
+ self.root_path = 'book/index.html'
+ self.initial_paths = %w(
+ reference.html
+ collections/index.html
+ std/index.html
+ unicode/index.html)
+ self.links = {
+ home: 'http://www.rust-lang.org/',
+ code: 'https://github.com/rust-lang/rust'
+ }
+
+ html_filters.push 'rust/entries', 'rust/clean_html'
+
+ options[:only_patterns] = [
+ /\Abook\//,
+ /\Acollections\//,
+ /\Astd\//,
+ /\Aunicode\// ]
+
+ options[:skip] = %w(book/README.html)
+ options[:skip_patterns] = [/(?(url) do
+ url.sub! %r{(#{Rust.base_url}.+/)\z}, '\1index.html'
+ url.sub! '/unicode/u_str', '/unicode/str/'
+ url
+ end
+
+ options[:attribution] = <<-HTML
+ © 2011-2015 The Rust Project Developers
+ Licensed under the Apache License, Version 2.0 or the MIT license, at your option.
+ HTML
+
+ private
+
+ REDIRECT_RGX = /http-equiv="refresh"/i
+ NOT_FOUND_RGX = /