Repository: lambda-fairy/maud (rust)

Issue:
Toggle `unstable` flag automatically based on rustc version
The library should no longer expose a public `unstable` feature flag, and instead infer it using the [`rustc_version`](https://crates.io/crates/rustc_version) crate.

Pull request:
Enable stable support 🎉
Infer whether nightly features are available using `rustc_version`. If this fails, assume stable.

Closes #214

Patch:
diff --git a/.travis.yml b/.travis.yml
index 31bd92f..db09cfd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,15 +9,7 @@ jobs:
         - |
           RUN_CLIPPY=true
           rustup component add clippy --toolchain=nightly || RUN_CLIPPY=false
-        # As of right now, these need to be tested individually becuase `--workspace`
-        # (which is implied by `workspace = true`) cannot be used with `--features`
-        # This _may_ be resolved with the `--package` flag, however this requires
-        # opting in to a `-Z` unstable feature as of time of commit.
-        #
-        # Follow https://github.com/rust-lang/cargo/issues/5364 for updates.
-        - cargo test --manifest-path maud/Cargo.toml --features actix-web,iron,rocket
-        - cargo test --manifest-path maud_htmlescape/Cargo.toml
-        - cargo test --manifest-path maud_macros/Cargo.toml
+        - cargo test --all --all-features
         - |
           if $RUN_CLIPPY; then
             CLIPPY_STATUS=0
@@ -26,12 +18,14 @@ jobs:
             done
             (exit $CLIPPY_STATUS)
           fi
-    - name: "Unstable"
+    - name: "Stable"
+      rust: stable
       script:
-        - cargo test --all --all-features
+        # Skip `--all-features` because stable Rocket isn't released yet
+        - cargo test --all
     - name: "Benchmarks"
       script:
-        - (cd benchmarks && cargo test --benches --features unstable)
+        - (cd benchmarks && cargo test --benches)
     - name: "Documentation"
       script:
         - (cd docs && make -j$(nproc))
diff --git a/README.md b/README.md
index 9a7454b..20a008e 100644
--- a/README.md
+++ b/README.md
@@ -7,18 +7,9 @@
 
 Maud is an HTML template engine for Rust. It's implemented as a macro, `html!`, which compiles your markup to specialized Rust code. This unique approach makes Maud templates blazing fast, super type-safe, and easy to deploy.
 
-Note that Maud depends on the unstable [procedural macro API][rustissue], and so requires the nightly version of Rust.
-
 For more info on Maud, see the [official book][book].
 
 [book]: https://maud.lambda.xyz/
 [booksrc]: https://github.com/lambda-fairy/maud/tree/master/docs
 [apiref]: https://docs.rs/maud/
 [changelog]: https://github.com/lambda-fairy/maud/blob/master/CHANGELOG.md
-[rustissue]: https://github.com/rust-lang/rust/issues/38356
-
-## Stability
-
-As of version 0.11, I am satisfied with the core syntax and semantics of the library. Development at this stage is focused on adding features and fixing bugs.
-
-The underlying procedural macro API is still unstable though, so updating your compiler may break things. Please file an issue when this happens!
diff --git a/benchmarks/Cargo.toml b/benchmarks/Cargo.toml
index fe8ec2f..adcce70 100644
--- a/benchmarks/Cargo.toml
+++ b/benchmarks/Cargo.toml
@@ -4,9 +4,6 @@ version = "0.1.2"
 authors = ["Chris Wong <lambda.fairy@gmail.com>"]
 edition = "2018"
 
-[features]
-unstable = ["maud/unstable"]
-
 [dependencies]
 maud = { path = "../maud" }
 handlebars = "*"
diff --git a/maud/Cargo.toml b/maud/Cargo.toml
index af8f9ef..91555b1 100644
--- a/maud/Cargo.toml
+++ b/maud/Cargo.toml
@@ -17,9 +17,6 @@ default = []
 # Web framework integrations
 actix-web = ["actix-web-dep", "futures"]
 
-# Unstable features
-unstable = []
-
 [dependencies]
 maud_htmlescape = { version = "0.17.0", path = "../maud_htmlescape" }
 maud_macros = { version = "0.22.0", path = "../maud_macros" }
@@ -28,6 +25,9 @@ rocket = { version = ">= 0.3, < 0.5", optional = true }
 futures = { version = "0.3.0", optional = true }
 actix-web-dep = { version = "2.0.0", optional = true, default-features = false, package = "actix-web" }
 
+[build-dependencies]
+rustc_version = "0.2.3"
+
 [dev-dependencies]
 trybuild = { version = "1.0.33", features = ["diff"] }
 
diff --git a/maud/build.rs b/maud/build.rs
new file mode 100644
index 0000000..e842d94
--- /dev/null
+++ b/maud/build.rs
@@ -0,0 +1,8 @@
+use rustc_version::{version_meta, Channel};
+
+fn main() {
+    match version_meta().map(|v| v.channel).unwrap_or(Channel::Stable) {
+        Channel::Dev | Channel::Nightly => println!("cargo:rustc-cfg=unstable"),
+        Channel::Beta | Channel::Stable => {}
+    }
+}
diff --git a/maud/src/lib.rs b/maud/src/lib.rs
index 7a4f53f..9100587 100644
--- a/maud/src/lib.rs
+++ b/maud/src/lib.rs
@@ -1,4 +1,4 @@
-#![cfg_attr(feature = "unstable", feature(min_specialization))]
+#![cfg_attr(unstable, feature(min_specialization))]
 
 //! A macro for writing HTML templates.
 //!
@@ -78,28 +78,28 @@ pub trait Render {
     }
 }
 
-#[cfg(not(feature = "unstable"))]
+#[cfg(not(unstable))]
 impl<T: fmt::Display + ?Sized> Render for T {
     fn render_to(&self, w: &mut String) {
         let _ = write!(Escaper::new(w), "{}", self);
     }
 }
 
-#[cfg(feature = "unstable")]
+#[cfg(unstable)]
 impl<T: fmt::Display + ?Sized> Render for T {
     default fn render_to(&self, w: &mut String) {
         let _ = write!(Escaper::new(w), "{}", self);
     }
 }
 
-#[cfg(feature = "unstable")]
+#[cfg(unstable)]
 impl Render for String {
     fn render_to(&self, w: &mut String) {
         let _ = Escaper::new(w).write_str(self);
     }
 }
 
-#[cfg(feature = "unstable")]
+#[cfg(unstable)]
 impl Render for str {
     fn render_to(&self, w: &mut String) {
         let _ = Escaper::new(w).write_str(self);
diff --git a/rust-toolchain b/rust-toolchain
deleted file mode 100644
index bf867e0..0000000
--- a/rust-toolchain
+++ /dev/null
@@ -1,1 +0,0 @@
-nightly


Test patch:
diff --git a/maud/tests/errors.rs b/maud/tests/errors.rs
index fa31fd1..e294f7d 100644
--- a/maud/tests/errors.rs
+++ b/maud/tests/errors.rs
@@ -1,3 +1,6 @@
+// Diagnostics look slightly different on stable Rust
+#![cfg(unstable)]
+
 use trybuild::TestCases;
 
 #[test]