Repository: serde-rs/json (language: rust)

Issue:
No_std support
_Here be dragons_

This is an attempt at resolving #362.

---

This pr is far from complete. The goal is to start a discussion on what the effort would be to correctly add no_std support to serde_json.

What was done so far:
- Added a std feature that is enabled by default
- Added a no_std feature so that we can enable specific dependencie features as required (e.g. alloc in serde)
- Added no_std support for the deserializer

Build with no_std:
```bash
cargo build --no-default-features --features no_std

# building for a different target
rustup target add thumbv7m-none-eabi
cargo build --no-default-features --features no_std --target thumbv7m-none-eabi
```

This was tested in another project were we use serde_json for json deserialization. We were able successfully compile with no_std support using the changes in this pr.

---
**Problems:**

The import logic can get messy:
- Nested imports makes it cleaner but they were only added in rust version 1.25
- We can probably try to reuse the import logic performed by serde. Although it's weird to depend on how another crate does imports

Requires nightly:
- Both serde `unstable` and `alloc` features require nightly

Cannot have dev-dependencies:
- There seems to be a [problem in cargo](https://github.com/rust-lang/cargo/issues/4866) where the dependencies and dev-dependecies features are unified. With the dev-dependecies I couldn't find a way to disable serde default features
- This also means that we cannot run the tests

Error handling becomes a bit more difficult, specially parts of the code that make heavy use of std::io::Error. 




Discussion / hints:
Thanks, looks like this is in the right direction.
Just an update.

I added no_std support for the Serializer. For now I skipped the PrettyFormatter and the the raw_value and arbitrary_precision features.

My main concern now is to figure out a way to handle dev-dependencies (since it overrides the features) so that I can run the tests.

When you have the time let me know if you have any suggestions for the code so far.
Is there anything that could be done to help you finish this PR ? I have huge interest in this feature. :smile: 
Right now the biggest problem is the fact that cargo unifies the `dependecies` and `dev-dependecies` which makes it difficult to run the tests. I don't really have a solution for that.

I would also need some input from @dtolnay to figure out how to proceed.

This merge request initially started from a previous project I was working on that was using serde_json. The project was using my [fork of serde_json](https://github.com/r-marques/json/tree/no_std_support) running on a cortex-m4f. In the meantime you can point cargo to my branch if you want to try it out.
Is your project running on a cortex-m4f open source ?
I could use some inspiration !
Unfortunately the project although in a running state was abandoned and is currently closed source. I don't know at this point if the company will ever open source it.

But the rust embedded working group has great resources and the [discovery book](https://rust-embedded.github.io/discovery/index.html) is actually using the cortex-m4f

Fix patch:
diff --git a/.travis.yml b/.travis.yml
index 707dee3f4..1571f801f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,6 +19,9 @@ matrix:
     - rust: stable
     - rust: beta
     - rust: 1.31.0
+    - rust: 1.36.0
+      script:
+        - cargo build --no-default-features --features alloc
 
     - rust: nightly
       name: Clippy
diff --git a/Cargo.toml b/Cargo.toml
index a987aa0a5..e942f55d5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,9 +16,9 @@ travis-ci = { repository = "serde-rs/json" }
 appveyor = { repository = "serde-rs/json" }
 
 [dependencies]
-serde = "1.0.60"
+serde = { version = "1.0.60", default-features = false }
 indexmap = { version = "1.2", optional = true }
-itoa = "0.4.3"
+itoa = { version = "0.4.3", default-features = false }
 ryu = "1.0"
 
 [dev-dependencies]
@@ -42,7 +42,15 @@ features = ["raw_value"]
 ### FEATURES #################################################################
 
 [features]
-default = []
+default = ["std"]
+
+std = ["serde/std", "itoa/std"]
+
+# Provide integration for heap-allocated collections without depending on the
+# rest of the Rust standard library.
+# NOTE: Disabling both `std` *and* `alloc` features is not supported yet.
+# Available on Rust 1.36+.
+alloc = ["serde/alloc"]
 
 # Use a different representation for the map type of serde_json::Value.
 # This allows data to be read into a Value and written back to a JSON string
diff --git a/src/de.rs b/src/de.rs
index 5c9c20b20..c71b796eb 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -1,10 +1,9 @@
 //! Deserialize JSON data to a Rust data structure.
 
-use std::io;
-use std::marker::PhantomData;
-use std::result;
-use std::str::FromStr;
-use std::{i32, u64};
+use lib::str::FromStr;
+use lib::*;
+
+use io;
 
 use serde::de::{self, Expected, Unexpected};
 
diff --git a/src/error.rs b/src/error.rs
index 5e3e8f064..b0876f71a 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,10 +1,9 @@
 //! When serializing or deserializing JSON goes wrong.
 
-use std::error;
-use std::fmt::{self, Debug, Display};
-use std::io;
-use std::result;
-use std::str::FromStr;
+use lib::str::FromStr;
+use lib::*;
+
+use io;
 
 use serde::de;
 use serde::ser;
@@ -333,8 +332,8 @@ impl Display for ErrorCode {
     }
 }
 
-impl error::Error for Error {
-    fn source(&self) -> Option<&(error::Error + 'static)> {
+impl serde::de::StdError for Error {
+    fn source(&self) -> Option<&(serde::de::StdError + 'static)> {
         match self.err.code {
             ErrorCode::Io(ref err) => Some(err),
             _ => None,
diff --git a/src/features_check/error.rs b/src/features_check/error.rs
new file mode 100644
index 000000000..967904bd4
--- /dev/null
+++ b/src/features_check/error.rs
@@ -0,0 +1,1 @@
+"serde_json doesn't work without `alloc` or default `std` feature yet"
\ No newline at end of file
diff --git a/src/features_check/mod.rs b/src/features_check/mod.rs
new file mode 100644
index 000000000..5b27a1244
--- /dev/null
+++ b/src/features_check/mod.rs
@@ -0,0 +1,13 @@
+//! Shows a user-friendly compiler error on incompatible selected features.
+
+#[allow(unused_macros)]
+macro_rules! hide_from_rustfmt {
+    ($mod:item) => {
+        $mod
+    };
+}
+
+#[cfg(all(not(feature = "std"), not(feature = "alloc")))]
+hide_from_rustfmt! {
+    mod error;
+}
diff --git a/src/io/core.rs b/src/io/core.rs
new file mode 100644
index 000000000..d899eba1c
--- /dev/null
+++ b/src/io/core.rs
@@ -0,0 +1,158 @@
+//! Reimplements core logic and types from `std::io` in an `alloc`-friendly
+//! fashion.
+#![cfg(not(feature = "std"))]
+
+use lib::*;
+
+pub enum ErrorKind {
+    InvalidData,
+    WriteZero,
+    Other,
+    UnexpectedEof,
+}
+
+impl ErrorKind {
+    #[inline]
+    fn as_str(&self) -> &'static str {
+        match self {
+            ErrorKind::InvalidData => "invalid data",
+            ErrorKind::WriteZero => "write zero",
+            ErrorKind::Other => "other os error",
+            ErrorKind::UnexpectedEof => "unexpected end of file",
+        }
+    }
+}
+
+pub struct Error {
+    repr: Repr,
+}
+
+enum Repr {
+    Simple(ErrorKind),
+    Custom(ErrorKind, Box<dyn serde::de::StdError + Send + Sync>),
+}
+
+impl Display for Error {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match &self.repr {
+            Repr::Custom(_, msg) => write!(fmt, "{}", msg),
+            Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()),
+        }
+    }
+}
+
+impl Debug for Error {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        Display::fmt(self, fmt)
+    }
+}
+
+impl serde::de::StdError for Error {}
+
+impl From<ErrorKind> for Error {
+    #[inline]
+    fn from(kind: ErrorKind) -> Error {
+        Error {
+            repr: Repr::Simple(kind),
+        }
+    }
+}
+
+impl Error {
+    #[inline]
+    pub fn new<E>(kind: ErrorKind, error: E) -> Error
+    where
+        E: Into<Box<dyn serde::de::StdError + Send + Sync>>,
+    {
+        Error {
+            repr: Repr::Custom(kind, error.into()),
+        }
+    }
+}
+
+pub type Result<T> = result::Result<T, Error>;
+
+pub trait Write {
+    fn write(&mut self, buf: &[u8]) -> Result<usize>;
+
+    fn write_all(&mut self, mut buf: &[u8]) -> Result<()> {
+        while !buf.is_empty() {
+            match self.write(buf) {
+                Ok(0) => {
+                    return Err(Error::new(
+                        ErrorKind::WriteZero,
+                        "failed to write whole buffer",
+                    ))
+                }
+                Ok(n) => buf = &buf[n..],
+                Err(e) => return Err(e),
+            }
+        }
+        Ok(())
+    }
+
+    fn flush(&mut self) -> Result<()>;
+}
+
+impl<W: Write> Write for &mut W {
+    #[inline]
+    fn write(&mut self, buf: &[u8]) -> Result<usize> {
+        (*self).write(buf)
+    }
+
+    #[inline]
+    fn write_all(&mut self, buf: &[u8]) -> Result<()> {
+        (*self).write_all(buf)
+    }
+
+    #[inline]
+    fn flush(&mut self) -> Result<()> {
+        (*self).flush()
+    }
+}
+
+impl Write for Vec<u8> {
+    #[inline]
+    fn write(&mut self, buf: &[u8]) -> Result<usize> {
+        self.extend_from_slice(buf);
+        Ok(buf.len())
+    }
+
+    #[inline]
+    fn write_all(&mut self, buf: &[u8]) -> Result<()> {
+        self.extend_from_slice(buf);
+        Ok(())
+    }
+
+    #[inline]
+    fn flush(&mut self) -> Result<()> {
+        Ok(())
+    }
+}
+
+pub trait Read {
+    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
+    fn bytes(self) -> Bytes<Self>
+    where
+        Self: Sized,
+    {
+        Bytes { inner: self }
+    }
+}
+
+pub struct Bytes<R> {
+    inner: R,
+}
+
+impl<R: Read> Iterator for Bytes<R> {
+    type Item = Result<u8>;
+
+    fn next(&mut self) -> Option<Result<u8>> {
+        let mut byte = 0;
+        match self.inner.read(slice::from_mut(&mut byte)) {
+            Ok(0) => None,
+            Ok(..) => Some(Ok(byte)),
+            Err(e) => Some(Err(e)),
+        }
+    }
+}
diff --git a/src/io/mod.rs b/src/io/mod.rs
new file mode 100644
index 000000000..c64822359
--- /dev/null
+++ b/src/io/mod.rs
@@ -0,0 +1,19 @@
+//! A tiny, `no_std`-friendly facade around `std::io`.
+//! Reexports types from `std` when available; otherwise reimplements and
+//! provides some of the core logic.
+//!
+//! The main reason that `std::io` hasn't found itself reexported as part of
+//! the `core` crate is the `std::io::{Read, Write}` traits' reliance on
+//! `std::io::Error`, which may contain internally a heap-allocated `Box<Error>`
+//! and/or now relying on OS-specific `std::backtrace::Backtrace`.
+
+pub use self::imp::{Bytes, Error, ErrorKind, Read, Result, Write};
+
+mod core;
+
+mod imp {
+    #[cfg(not(feature = "std"))]
+    pub use super::core::*;
+    #[cfg(feature = "std")]
+    pub use std::io::*;
+}
diff --git a/src/iter.rs b/src/iter.rs
index 1a9a954d1..53cbcbc05 100644
--- a/src/iter.rs
+++ b/src/iter.rs
@@ -1,4 +1,4 @@
-use std::io;
+use io;
 
 pub struct LineColIterator<I> {
     iter: I,
diff --git a/src/lib.rs b/src/lib.rs
index 17e3689e5..4d6512d42 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -331,6 +331,7 @@
     must_use_candidate,
 ))]
 #![deny(missing_docs)]
+#![cfg_attr(not(feature = "std"), no_std)]
 
 #[macro_use]
 extern crate serde;
@@ -339,6 +340,67 @@ extern crate indexmap;
 extern crate itoa;
 extern crate ryu;
 
+////////////////////////////////////////////////////////////////////////////////
+
+#[cfg(not(feature = "std"))]
+extern crate alloc;
+
+/// A facade around all the types we need from the `std`, `core`, and `alloc`
+/// crates. This avoids elaborate import wrangling having to happen in every
+/// module.
+mod lib {
+    mod core {
+        #[cfg(not(feature = "std"))]
+        pub use core::*;
+        #[cfg(feature = "std")]
+        pub use std::*;
+    }
+
+    pub use self::core::{char, str};
+    pub use self::core::{cmp, mem, num, slice};
+
+    pub use self::core::{borrow, iter, ops};
+
+    pub use self::core::cell::{Cell, RefCell};
+    pub use self::core::clone::{self, Clone};
+    pub use self::core::convert::{self, From, Into};
+    pub use self::core::default::{self, Default};
+    pub use self::core::fmt::{self, Debug, Display};
+    pub use self::core::hash::{self, Hash};
+    pub use self::core::marker::{self, PhantomData};
+    pub use self::core::result::{self, Result};
+
+    #[cfg(not(feature = "std"))]
+    pub use alloc::borrow::{Cow, ToOwned};
+    #[cfg(feature = "std")]
+    pub use std::borrow::{Cow, ToOwned};
+
+    #[cfg(not(feature = "std"))]
+    pub use alloc::string::{String, ToString};
+    #[cfg(feature = "std")]
+    pub use std::string::{String, ToString};
+
+    #[cfg(not(feature = "std"))]
+    pub use alloc::vec::{self, Vec};
+    #[cfg(feature = "std")]
+    pub use std::vec::{self, Vec};
+
+    #[cfg(not(feature = "std"))]
+    pub use alloc::boxed::Box;
+    #[cfg(feature = "std")]
+    pub use std::boxed::Box;
+
+    #[cfg(not(feature = "std"))]
+    pub use alloc::collections::{btree_map, BTreeMap};
+    #[cfg(feature = "std")]
+    pub use std::collections::{btree_map, BTreeMap};
+
+    #[cfg(feature = "std")]
+    pub use std::error;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 #[doc(inline)]
 pub use self::de::{from_reader, from_slice, from_str, Deserializer, StreamDeserializer};
 #[doc(inline)]
@@ -355,8 +417,8 @@ pub use self::value::{from_value, to_value, Map, Number, Value};
 macro_rules! try {
     ($e:expr) => {
         match $e {
-            ::std::result::Result::Ok(val) => val,
-            ::std::result::Result::Err(err) => return ::std::result::Result::Err(err),
+            ::lib::Result::Ok(val) => val,
+            ::lib::Result::Err(err) => return ::lib::Result::Err(err),
         }
     };
 }
@@ -370,6 +432,9 @@ pub mod map;
 pub mod ser;
 pub mod value;
 
+mod features_check;
+
+mod io;
 mod iter;
 mod number;
 mod read;
diff --git a/src/map.rs b/src/map.rs
index 66fc991cc..0d717616c 100644
--- a/src/map.rs
+++ b/src/map.rs
@@ -6,16 +6,13 @@
 //! [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
 //! [`IndexMap`]: https://docs.rs/indexmap/*/indexmap/map/struct.IndexMap.html
 
-use serde::{de, ser};
-use std::borrow::Borrow;
-use std::fmt::{self, Debug};
-use std::hash::Hash;
-use std::iter::FromIterator;
-use std::ops;
+use lib::borrow::Borrow;
+use lib::iter::FromIterator;
+use lib::*;
+
 use value::Value;
 
-#[cfg(not(feature = "preserve_order"))]
-use std::collections::{btree_map, BTreeMap};
+use serde::{de, ser};
 
 #[cfg(feature = "preserve_order")]
 use indexmap::{self, IndexMap};
@@ -152,7 +149,7 @@ impl Map<String, Value> {
         #[cfg(feature = "preserve_order")]
         use indexmap::map::Entry as EntryImpl;
         #[cfg(not(feature = "preserve_order"))]
-        use std::collections::btree_map::Entry as EntryImpl;
+        use lib::btree_map::Entry as EntryImpl;
 
         match self.map.entry(key.into()) {
             EntryImpl::Vacant(vacant) => Entry::Vacant(VacantEntry { vacant: vacant }),
diff --git a/src/number.rs b/src/number.rs
index 84672e9e0..fc75f4230 100644
--- a/src/number.rs
+++ b/src/number.rs
@@ -1,7 +1,8 @@
+use lib::*;
+
 use error::Error;
 use serde::de::{self, Unexpected, Visitor};
 use serde::{Deserialize, Deserializer, Serialize, Serializer};
-use std::fmt::{self, Debug, Display};
 
 #[cfg(feature = "arbitrary_precision")]
 use itoa;
diff --git a/src/raw.rs b/src/raw.rs
index d445960f1..9e635cde9 100644
--- a/src/raw.rs
+++ b/src/raw.rs
@@ -1,5 +1,4 @@
-use std::fmt::{self, Debug, Display};
-use std::mem;
+use lib::*;
 
 use serde::de::value::BorrowedStrDeserializer;
 use serde::de::{
diff --git a/src/read.rs b/src/read.rs
index 71b050413..6e795c9c8 100644
--- a/src/read.rs
+++ b/src/read.rs
@@ -1,5 +1,7 @@
-use std::ops::Deref;
-use std::{char, cmp, io, str};
+use lib::ops::Deref;
+use lib::*;
+
+use io;
 
 #[cfg(feature = "raw_value")]
 use serde::de::Visitor;
diff --git a/src/ser.rs b/src/ser.rs
index cfae3815f..ffc112c79 100644
--- a/src/ser.rs
+++ b/src/ser.rs
@@ -1,9 +1,9 @@
 //! Serialize a Rust data structure into JSON data.
 
-use std::fmt;
-use std::io;
-use std::num::FpCategory;
-use std::str;
+use lib::num::FpCategory;
+use lib::*;
+
+use io;
 
 use super::error::{Error, ErrorCode, Result};
 use serde::ser::{self, Impossible, Serialize};
@@ -460,7 +460,7 @@ where
     where
         T: fmt::Display,
     {
-        use std::fmt::Write;
+        use self::fmt::Write;
 
         struct Adapter<'ser, W: 'ser, F: 'ser> {
             writer: &'ser mut W,
@@ -1638,7 +1638,9 @@ pub trait Formatter {
     where
         W: io::Write,
     {
-        itoa::write(writer, value).map(drop)
+        let mut buffer = itoa::Buffer::new();
+        let s = buffer.format(value);
+        writer.write_all(s.as_bytes())
     }
 
     /// Writes an integer value like `-123` to the specified writer.
@@ -1647,7 +1649,9 @@ pub trait Formatter {
     where
         W: io::Write,
     {
-        itoa::write(writer, value).map(drop)
+        let mut buffer = itoa::Buffer::new();
+        let s = buffer.format(value);
+        writer.write_all(s.as_bytes())
     }
 
     /// Writes an integer value like `-123` to the specified writer.
@@ -1656,7 +1660,9 @@ pub trait Formatter {
     where
         W: io::Write,
     {
-        itoa::write(writer, value).map(drop)
+        let mut buffer = itoa::Buffer::new();
+        let s = buffer.format(value);
+        writer.write_all(s.as_bytes())
     }
 
     /// Writes an integer value like `-123` to the specified writer.
@@ -1665,7 +1671,9 @@ pub trait Formatter {
     where
         W: io::Write,
     {
-        itoa::write(writer, value).map(drop)
+        let mut buffer = itoa::Buffer::new();
+        let s = buffer.format(value);
+        writer.write_all(s.as_bytes())
     }
 
     /// Writes an integer value like `123` to the specified writer.
@@ -1674,7 +1682,9 @@ pub trait Formatter {
     where
         W: io::Write,
     {
-        itoa::write(writer, value).map(drop)
+        let mut buffer = itoa::Buffer::new();
+        let s = buffer.format(value);
+        writer.write_all(s.as_bytes())
     }
 
     /// Writes an integer value like `123` to the specified writer.
@@ -1683,7 +1693,9 @@ pub trait Formatter {
     where
         W: io::Write,
     {
-        itoa::write(writer, value).map(drop)
+        let mut buffer = itoa::Buffer::new();
+        let s = buffer.format(value);
+        writer.write_all(s.as_bytes())
     }
 
     /// Writes an integer value like `123` to the specified writer.
@@ -1692,7 +1704,9 @@ pub trait Formatter {
     where
         W: io::Write,
     {
-        itoa::write(writer, value).map(drop)
+        let mut buffer = itoa::Buffer::new();
+        let s = buffer.format(value);
+        writer.write_all(s.as_bytes())
     }
 
     /// Writes an integer value like `123` to the specified writer.
@@ -1701,7 +1715,9 @@ pub trait Formatter {
     where
         W: io::Write,
     {
-        itoa::write(writer, value).map(drop)
+        let mut buffer = itoa::Buffer::new();
+        let s = buffer.format(value);
+        writer.write_all(s.as_bytes())
     }
 
     /// Writes a floating point value like `-31.26e+12` to the specified writer.
diff --git a/src/value/de.rs b/src/value/de.rs
index c1f0810dd..c0de6b887 100644
--- a/src/value/de.rs
+++ b/src/value/de.rs
@@ -1,8 +1,5 @@
-use std::borrow::Cow;
-use std::fmt;
-use std::slice;
-use std::str;
-use std::vec;
+use lib::str::FromStr;
+use lib::*;
 
 use serde;
 use serde::de::{
@@ -134,7 +131,7 @@ impl<'de> Deserialize<'de> for Value {
     }
 }
 
-impl str::FromStr for Value {
+impl FromStr for Value {
     type Err = Error;
     fn from_str(s: &str) -> Result<Value, Error> {
         super::super::de::from_str(s)
diff --git a/src/value/from.rs b/src/value/from.rs
index 2b743d2af..fdc3b6660 100644
--- a/src/value/from.rs
+++ b/src/value/from.rs
@@ -1,4 +1,5 @@
-use std::borrow::Cow;
+use lib::iter::FromIterator;
+use lib::*;
 
 use super::Value;
 use map::Map;
@@ -182,7 +183,7 @@ impl<'a, T: Clone + Into<Value>> From<&'a [T]> for Value {
     }
 }
 
-impl<T: Into<Value>> ::std::iter::FromIterator<T> for Value {
+impl<T: Into<Value>> FromIterator<T> for Value {
     /// Convert an iteratable type to a `Value`
     ///
     /// # Examples
diff --git a/src/value/index.rs b/src/value/index.rs
index 47990a547..00f71f8e7 100644
--- a/src/value/index.rs
+++ b/src/value/index.rs
@@ -1,5 +1,4 @@
-use std::fmt;
-use std::ops;
+use lib::*;
 
 use super::Value;
 use map::Map;
@@ -135,7 +134,7 @@ mod private {
     pub trait Sealed {}
     impl Sealed for usize {}
     impl Sealed for str {}
-    impl Sealed for String {}
+    impl Sealed for super::String {}
     impl<'a, T: ?Sized> Sealed for &'a T where T: Sealed {}
 }
 
diff --git a/src/value/mod.rs b/src/value/mod.rs
index fee9939fc..f0db11f42 100644
--- a/src/value/mod.rs
+++ b/src/value/mod.rs
@@ -90,10 +90,9 @@
 //! [from_slice]: https://docs.serde.rs/serde_json/de/fn.from_slice.html
 //! [from_reader]: https://docs.serde.rs/serde_json/de/fn.from_reader.html
 
-use std::fmt::{self, Debug};
-use std::io;
-use std::mem;
-use std::str;
+use lib::*;
+
+use io;
 
 use serde::de::DeserializeOwned;
 use serde::ser::Serialize;
diff --git a/src/value/partial_eq.rs b/src/value/partial_eq.rs
index cfcf95706..7fc64c3bd 100644
--- a/src/value/partial_eq.rs
+++ b/src/value/partial_eq.rs
@@ -1,3 +1,5 @@
+use lib::*;
+
 use super::Value;
 
 fn eq_i64(value: &Value, other: i64) -> bool {
diff --git a/src/value/ser.rs b/src/value/ser.rs
index b0e09beb5..eaba74bcb 100644
--- a/src/value/ser.rs
+++ b/src/value/ser.rs
@@ -1,3 +1,5 @@
+use lib::*;
+
 use serde::ser::Impossible;
 use serde::{self, Serialize};
 


Test patch: