[RFC] Add an option to allow C99-like comments.

This would introduce a `serde_json::Options` type that could be
passed to `from_str`, `from_bytes, or `from_reader`. Initially,
the only option would be `allow_comments`, though I would also
like to add support for `allow_trailing_commas` going forward.

I read through https://github.com/serde-rs/json/issues/168 and
saw that this was rejected in the past, but I was curious if we
could revisit this issue. The suggestion on the issue was to use
[Hjson](https://github.com/hjson/hjson-rust), which I think is
"too loose" for most peoples' taste. Also it has [not been updated to
target serde 1.0.x yet](https://github.com/hjson/hjson-rust/issues/6).

Allowing C99-like comments and trailing commas results in an input
language that is still valid ECMAScript (it's just version 5.1 instead
of 3), which cannot be said for things like Hjson. Because it is
familiar to JavaScript developers, I have seen it used as an input
language in a number of domains. For example, all settings files in VS
Code appear to allow this: it even has a special language mode to
recognize this named "JSON with Comments (jsonc)."

It is fairly common for JSON parsers to allow options like this.
For example, here is the full set of options supported by Folly's
(C++ library) JSON parser:

https://github.com/facebook/folly/blob/74ea53886c3333eda4eaf457d538a678ceaa5add/folly/json.h#L56-L72

Google's Gson parser has a `setLenient()` option:

https://github.com/google/gson/blob/9d44cbc19a73b45971c4ecb33c8d34d673afa210/gson/src/main/java/com/google/gson/stream/JsonReader.java#L296-L324

There's also https://json5.org/, which tries to formalize the leniency.

If `serde_json` is not on board with this direction, I guess I should
fork this repo and create `serde_jsonc` or `serde_lenient_json`? I would
prefer not to do that, but if you want to keep `serde_json` so that it
only supports RFC 8259 or whatever it is, then I suppose forking is the
best way forward.

One final note: although https://github.com/serde-rs/json/issues/168
notes that comments were "explicitly excluded from JSON," it doesn't
mean it was the right choice. I've been arguing against it for years
now: http://bolinfest.com/essays/json.html.
diff --git a/src/de.rs b/src/de.rs
index 0a91a0b..32f2129 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -20,11 +20,26 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
+/// Options for a Deserializer.
+pub struct Options {
+    /// Whether to allow // and /* */ comments in the JSON.
+    pub allow_comments: bool,
+}
+
+impl Default for Options {
+    fn default() -> Self {
+        Self {
+            allow_comments: false,
+        }
+    }
+}
+
 /// A structure that deserializes JSON into Rust values.
 pub struct Deserializer<R> {
     read: R,
     scratch: Vec<u8>,
     remaining_depth: u8,
+    options: Options,
 }
 
 impl<'de, R> Deserializer<R>
@@ -44,6 +59,17 @@
             read: read,
             scratch: Vec::new(),
             remaining_depth: 128,
+            options: Options::default(),
+        }
+    }
+
+    /// Up for discussion...
+    pub fn new_with_options(read: R, options: Options) -> Self {
+        Deserializer {
+            read: read,
+            scratch: Vec::new(),
+            remaining_depth: 128,
+            options,
         }
     }
 }
@@ -177,6 +203,73 @@
     /// Returns the first non-whitespace byte without consuming it, or `None` if
     /// EOF is encountered.
     fn parse_whitespace(&mut self) -> Result<Option<u8>> {
+        if self.options.allow_comments {
+            // Consume comments as if they were whitespace.
+            loop {
+                match try!(self.parse_strict_whitespace()) {
+                    Some(b'/') => {
+                        self.eat_char();
+                        match try!(self.peek()) {
+                            Some(b'/') => {
+                                // TODO: Read until newline.
+                                loop {
+                                    match try!(self.peek()) {
+                                        Some(b'\n') => {
+                                            self.eat_char();
+                                            break;
+                                        }
+                                        Some(_) => {
+                                            self.eat_char();
+                                        }
+                                        None => {
+                                            return Ok(None);
+                                        }
+                                    }
+                                }
+                            }
+                            Some(b'*') => loop {
+                                match try!(self.peek()) {
+                                    Some(b'*') => {
+                                        self.eat_char();
+                                        match try!(self.peek()) {
+                                            Some(b'/') => {
+                                                self.eat_char();
+                                                break;
+                                            }
+                                            Some(_) => self.eat_char(),
+                                            None => {
+                                                return Err(self.peek_error(
+                                                    ErrorCode::EofWhileParsingBlockComment,
+                                                ));
+                                            }
+                                        }
+                                    }
+                                    Some(_) => {
+                                        self.eat_char();
+                                    }
+                                    None => {
+                                        return Err(
+                                            self.peek_error(ErrorCode::EofWhileParsingBlockComment)
+                                        );
+                                    }
+                                }
+                            },
+                            _ => {
+                                return Err(self.peek_error(ErrorCode::ExpectedCommentSlashOrStar));
+                            }
+                        };
+                    }
+                    other => {
+                        return Ok(other);
+                    }
+                }
+            }
+        } else {
+            self.parse_strict_whitespace()
+        }
+    }
+
+    fn parse_strict_whitespace(&mut self) -> Result<Option<u8>> {
         loop {
             match try!(self.peek()) {
                 Some(b' ') | Some(b'\n') | Some(b'\t') | Some(b'\r') => {
@@ -2103,6 +2196,19 @@
     Ok(value)
 }
 
+fn from_trait_with_options<'de, R, T>(read: R, options: Options) -> Result<T>
+where
+    R: Read<'de>,
+    T: de::Deserialize<'de>,
+{
+    let mut de = Deserializer::new_with_options(read, options);
+    let value = try!(de::Deserialize::deserialize(&mut de));
+
+    // Make sure the whole stream has been consumed.
+    try!(de.end());
+    Ok(value)
+}
+
 /// Deserialize an instance of type `T` from an IO stream of JSON.
 ///
 /// The content of the IO stream is deserialized directly from the stream
@@ -2254,3 +2360,11 @@
 {
     from_trait(read::StrRead::new(s))
 }
+
+/// For review...
+pub fn from_str_with_options<'a, T>(s: &'a str, options: Options) -> Result<T>
+where
+    T: de::Deserialize<'a>,
+{
+    from_trait_with_options(read::StrRead::new(s), options)
+}
diff --git a/src/error.rs b/src/error.rs
index 0508dd7..1135ac9 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -3,8 +3,8 @@
 use std::error;
 use std::fmt::{self, Debug, Display};
 use std::io;
-use std::str::FromStr;
 use std::result;
+use std::str::FromStr;
 
 use serde::de;
 use serde::ser;
@@ -51,11 +51,13 @@
         match self.err.code {
             ErrorCode::Message(_) => Category::Data,
             ErrorCode::Io(_) => Category::Io,
-            ErrorCode::EofWhileParsingList
+            ErrorCode::EofWhileParsingBlockComment
+            | ErrorCode::EofWhileParsingList
             | ErrorCode::EofWhileParsingObject
             | ErrorCode::EofWhileParsingString
             | ErrorCode::EofWhileParsingValue => Category::Eof,
             ErrorCode::ExpectedColon
+            | ErrorCode::ExpectedCommentSlashOrStar
             | ErrorCode::ExpectedListCommaOrEnd
             | ErrorCode::ExpectedObjectCommaOrEnd
             | ErrorCode::ExpectedObjectOrArray
@@ -187,6 +189,9 @@
     /// Some IO error occurred while serializing or deserializing.
     Io(io::Error),
 
+    /// Saw an opening `'/*'` without a closing `'*/'`.
+    EofWhileParsingBlockComment,
+
     /// EOF while parsing a list.
     EofWhileParsingList,
 
@@ -202,6 +207,10 @@
     /// Expected this character to be a `':'`.
     ExpectedColon,
 
+    /// Saw a `'/'` while parsing whitespace, so expected it to be
+    /// followed by either `'/'` or `'*'`.
+    ExpectedCommentSlashOrStar,
+
     /// Expected this character to be either a `','` or a `']'`.
     ExpectedListCommaOrEnd,
 
@@ -303,11 +312,15 @@
         match *self {
             ErrorCode::Message(ref msg) => f.write_str(msg),
             ErrorCode::Io(ref err) => Display::fmt(err, f),
+            ErrorCode::EofWhileParsingBlockComment => {
+                f.write_str("EOF while parsing a block comment")
+            }
             ErrorCode::EofWhileParsingList => f.write_str("EOF while parsing a list"),
             ErrorCode::EofWhileParsingObject => f.write_str("EOF while parsing an object"),
             ErrorCode::EofWhileParsingString => f.write_str("EOF while parsing a string"),
             ErrorCode::EofWhileParsingValue => f.write_str("EOF while parsing a value"),
             ErrorCode::ExpectedColon => f.write_str("expected `:`"),
+            ErrorCode::ExpectedCommentSlashOrStar => f.write_str("expected `/` or `*` after `/`"),
             ErrorCode::ExpectedListCommaOrEnd => f.write_str("expected `,` or `]`"),
             ErrorCode::ExpectedObjectCommaOrEnd => f.write_str("expected `,` or `}`"),
             ErrorCode::ExpectedObjectOrArray => f.write_str("expected `{` or `[`"),
diff --git a/src/lib.rs b/src/lib.rs
index f4270c7..a087491 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -327,7 +327,9 @@
 extern crate ryu;
 
 #[doc(inline)]
-pub use self::de::{from_reader, from_slice, from_str, Deserializer, StreamDeserializer};
+pub use self::de::{
+    from_reader, from_slice, from_str, from_str_with_options, Deserializer, Options, StreamDeserializer,
+};
 #[doc(inline)]
 pub use self::error::{Error, Result};
 #[doc(inline)]
diff --git a/tests/test.rs b/tests/test.rs
index 548f71a..77cf590 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -32,8 +32,8 @@
 use serde_bytes::{ByteBuf, Bytes};
 
 use serde_json::{
-    from_reader, from_slice, from_str, from_value, to_string, to_string_pretty, to_value, to_vec,
-    to_writer, Deserializer, Number, Value,
+    from_reader, from_slice, from_str, from_str_with_options, from_value, to_string,
+    to_string_pretty, to_value, to_vec, to_writer, Deserializer, Number, Options, Value,
 };
 
 macro_rules! treemap {
@@ -573,6 +573,37 @@
     assert_eq!(E::N(0), E::deserialize(Number::from(0)).unwrap());
 }
 
+#[test]
+fn test_parse_line_comments() {
+    let s = r#"
+    {
+        // Here is a comment.
+        "key": "value"
+    }// Here is a another comment at the end."#;
+    let options = Options {
+        allow_comments: true,
+    };
+    let value: Value = from_str_with_options(s, options).unwrap();
+    assert_eq!(value, json!({"key": "value"}));
+}
+
+#[test]
+fn test_parse_block_comments() {
+    let s = r#"
+    /*
+     Here is a comment up here.
+     */
+    {
+        /* And one in here. */
+        "key": "value"
+    }/* Some at the end... *//* ...back to back! */"#;
+    let options = Options {
+        allow_comments: true,
+    };
+    let value: Value = from_str_with_options(s, options).unwrap();
+    assert_eq!(value, json!({"key": "value"}));
+}
+
 fn test_parse_ok<T>(tests: Vec<(&str, T)>)
 where
     T: Clone + Debug + PartialEq + ser::Serialize + de::DeserializeOwned,