Repository: not-fl3/nanoserde
Pull request #105 (2024-06-09 17:23:43)

error in core changes
Now that [error in core is stabilizing](https://github.com/rust-lang/rust/issues/103765#issuecomment-2155244052), look at simplifying the feature flags (and making them additive). CI will fail here until it finishes stabilizing, but if we wait for this feature to land it should let us simplify a lot of the code, and only gate features that really rely on `std` (such as hashmaps) behind a `std` feature flag 

knickish: Looks like this [made it in to the 1.80 milestone](https://github.com/rust-lang/rust/pull/125951#issuecomment-2155311242), so hopefully can merge this after the 2024-07-25 when 1.80 is released
knickish: There are a lot of changes on master that are not on a crates-io release, might make sense to just merge this and leave the nightly requirement for now
knickish: @not-fl3 Any thoughts on doing this? Can swap the `no_std` flag for a `std` flag and make it a purely additive feature. Will require nightly to use without the `std` feature until 1.81 releases, but we currently require nightly for no std anyway. After that rust version releases, the nightly requirement can be dropped without a breaking change
not-fl3: As long as std is on by default, `nanoserde = *` is the only thing required and we don't bump the minimal rust version for a std use case - I am fine with any no_std change, I do not have any strong opinions here

Patch:
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index fe1da43..2c9c56b 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -80,7 +80,7 @@ jobs:
       - uses: actions-rs/cargo@v1
         with:
           command: test
-          args: --no-default-features --features ${{ matrix.feature }}
+          args: --no-default-features --features "std, ${{ matrix.feature }}"
 
   no_std_features:
     name: Test Individual Features NoStd
@@ -106,7 +106,7 @@ jobs:
       - uses: actions-rs/cargo@v1
         with:
           command: test
-          args: --no-default-features --features "no_std, ${{ matrix.feature }}"
+          args: --no-default-features --features "${{ matrix.feature }}"
 
   test:
     name: Test
@@ -153,4 +153,4 @@ jobs:
       - uses: actions-rs/cargo@v1
         with:
           command: test 
-          args: --features "no_std"
+          args: --no-default-features --features "binary, json, ron, toml"
diff --git a/Cargo.toml b/Cargo.toml
index ff100ce..98e79c2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,12 +22,12 @@ edition = "2018"
 repository = "https://github.com/not-fl3/nanoserde"
 
 [features]
-default = ["json", "binary", "ron", "toml"]
+default = ["json", "binary", "ron", "toml", "std"]
 json = ["dep:nanoserde-derive", "nanoserde-derive/json"]
 binary = ["dep:nanoserde-derive", "nanoserde-derive/binary"]
 ron = ["dep:nanoserde-derive", "nanoserde-derive/ron"]
 toml = []
-no_std = []
+std = []
 
 [dependencies]
 nanoserde-derive = { path = "derive", version = "=0.2.0", optional = true }
diff --git a/README.md b/README.md
index c67121f..b16a57b 100644
--- a/README.md
+++ b/README.md
@@ -56,7 +56,7 @@ For more examples take a look at [tests](/tests)
 
 All features are enabled by default. To enable only specific formats, import nanoserde using 
 ```toml
-nanoserde = { version = "*", default-features = false, features = [] }
+nanoserde = { version = "*", default-features = false, features = ["std", "{format feature name}"] }
 ```
 in your `Cargo.toml` and add one or more of the following crate features:
 
diff --git a/derive/Cargo.toml b/derive/Cargo.toml
index ceff924..ea32cb6 100644
--- a/derive/Cargo.toml
+++ b/derive/Cargo.toml
@@ -14,4 +14,3 @@ default = []
 json = []
 binary = []
 ron = []
-no_std = []
diff --git a/derive/src/lib.rs b/derive/src/lib.rs
index c8a5e79..0b76d55 100644
--- a/derive/src/lib.rs
+++ b/derive/src/lib.rs
@@ -1,4 +1,4 @@
-#![cfg_attr(feature = "no_std", no_std)]
+#![no_std]
 
 extern crate alloc;
 extern crate proc_macro;
diff --git a/derive/src/parse.rs b/derive/src/parse.rs
index 1122733..2c08164 100644
--- a/derive/src/parse.rs
+++ b/derive/src/parse.rs
@@ -4,10 +4,12 @@
 //! https://docs.rs/syn/0.15.44/syn/enum.Type.html
 //! https://ziglang.org/documentation/0.5.0/#toc-typeInfo
 
+use alloc::collections::BTreeSet;
 use core::iter::Peekable;
-use std::collections::HashSet;
-use std::num::IntErrorKind;
+use core::num::IntErrorKind;
 
+use alloc::borrow::ToOwned;
+use alloc::boxed::Box;
 use alloc::string::{String, ToString};
 use alloc::vec::Vec;
 use alloc::{format, vec};
@@ -16,6 +18,7 @@ use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
 
 #[derive(Debug, Clone)]
 pub struct Attribute {
+    #[allow(unused)]
     pub name: String,
     pub tokens: Vec<String>,
 }
@@ -37,6 +40,7 @@ pub struct Lifetime {
 #[derive(Debug, Clone)]
 pub struct Field {
     pub attributes: Vec<Attribute>,
+    #[allow(unused)]
     pub vis: Visibility,
     pub field_name: Option<String>,
     pub ty: Type,
@@ -1079,14 +1083,14 @@ fn next_attribute<T: Iterator<Item = TokenTree>>(
             (true, _) => {
                 attrs.push(Attribute {
                     name: name.clone(),
-                    tokens: std::mem::take(&mut attr_tokens),
+                    tokens: core::mem::take(&mut attr_tokens),
                 });
                 break;
             }
             (false, Some(",")) => {
                 attrs.push(Attribute {
                     name: name.clone(),
-                    tokens: std::mem::take(&mut attr_tokens),
+                    tokens: core::mem::take(&mut attr_tokens),
                 });
                 continue;
             }
@@ -1105,14 +1109,14 @@ fn next_attribute<T: Iterator<Item = TokenTree>>(
             (true, _) => {
                 attrs.push(Attribute {
                     name: name.clone(),
-                    tokens: std::mem::take(&mut attr_tokens),
+                    tokens: core::mem::take(&mut attr_tokens),
                 });
                 break;
             }
             (false, true) => {
                 attrs.push(Attribute {
                     name: name.clone(),
-                    tokens: std::mem::take(&mut attr_tokens),
+                    tokens: core::mem::take(&mut attr_tokens),
                 });
             }
             _ => {}
@@ -1402,7 +1406,7 @@ fn next_generic<T: Iterator<Item = TokenTree> + Clone>(
 
 fn get_all_bounds<T: Iterator<Item = TokenTree> + Clone>(source: &mut Peekable<T>) -> Vec<Generic> {
     let mut ret = Vec::new();
-    let mut already = HashSet::new();
+    let mut already = BTreeSet::new();
     if source.peek().map_or(false, |x| x.to_string() == "<") {
         source.next();
     } else {
diff --git a/derive/src/shared.rs b/derive/src/shared.rs
index 110f814..735bf75 100644
--- a/derive/src/shared.rs
+++ b/derive/src/shared.rs
@@ -1,4 +1,8 @@
-use alloc::string::String;
+use alloc::{
+    format,
+    string::{String, ToString},
+    vec::Vec,
+};
 
 #[cfg(any(feature = "binary", feature = "json"))]
 use crate::parse::{Enum, Struct};
diff --git a/src/lib.rs b/src/lib.rs
index 8384509..501e803 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -15,11 +15,8 @@
 //! `nanoserde` supports some serialization customisation with `#[nserde()]` attributes.
 //! For `#[nserde(..)]` supported attributes for each format check [Features support matrix](https://github.com/not-fl3/nanoserde#features-support-matrix)
 
-#![cfg_attr(feature = "no_std", no_std)]
-// Possibly stable in 1.65.
-// See: https://github.com/rust-lang/rust/pull/99917
-// and https://github.com/rust-lang/rust/issues/103765
-#![cfg_attr(feature = "no_std", feature(error_in_core))]
+#![cfg_attr(not(feature = "std"), no_std)]
+#![cfg_attr(not(feature = "std"), feature(error_in_core))]
 
 extern crate alloc;
 
diff --git a/src/serde_bin.rs b/src/serde_bin.rs
index 66c7fc1..b0af507 100644
--- a/src/serde_bin.rs
+++ b/src/serde_bin.rs
@@ -1,5 +1,11 @@
 use core::convert::TryInto;
 
+// remove this after 1.81 is live
+#[cfg(not(feature = "std"))]
+use core::error::Error;
+#[cfg(feature = "std")]
+use std::error::Error;
+
 use alloc::borrow::ToOwned;
 use alloc::boxed::Box;
 use alloc::collections::{BTreeMap, BTreeSet, LinkedList};
@@ -77,11 +83,7 @@ impl core::fmt::Display for DeBinErr {
     }
 }
 
-#[cfg(feature = "no_std")]
-impl core::error::Error for DeBinErr {}
-
-#[cfg(not(feature = "no_std"))]
-impl std::error::Error for DeBinErr {}
+impl Error for DeBinErr {}
 
 macro_rules! impl_ser_de_bin_for {
     ($ty:ident) => {
@@ -287,7 +289,7 @@ where
     }
 }
 
-#[cfg(not(feature = "no_std"))]
+#[cfg(feature = "std")]
 impl<T> SerBin for std::collections::HashSet<T>
 where
     T: SerBin,
@@ -301,7 +303,7 @@ where
     }
 }
 
-#[cfg(not(feature = "no_std"))]
+#[cfg(feature = "std")]
 impl<T> DeBin for std::collections::HashSet<T>
 where
     T: DeBin + core::hash::Hash + Eq,
@@ -529,7 +531,7 @@ where
     }
 }
 
-#[cfg(not(feature = "no_std"))]
+#[cfg(feature = "std")]
 impl<K, V> SerBin for std::collections::HashMap<K, V>
 where
     K: SerBin,
@@ -545,7 +547,7 @@ where
     }
 }
 
-#[cfg(not(feature = "no_std"))]
+#[cfg(feature = "std")]
 impl<K, V> DeBin for std::collections::HashMap<K, V>
 where
     K: DeBin + core::cmp::Eq + core::hash::Hash,
diff --git a/src/serde_json.rs b/src/serde_json.rs
index 4c42ec3..c668bf0 100644
--- a/src/serde_json.rs
+++ b/src/serde_json.rs
@@ -1,5 +1,11 @@
 use core::str::Chars;
 
+// remove this after 1.81 is live
+#[cfg(not(feature = "std"))]
+use core::error::Error;
+#[cfg(feature = "std")]
+use std::error::Error;
+
 use alloc::boxed::Box;
 use alloc::collections::{BTreeMap, BTreeSet, LinkedList};
 use alloc::format;
@@ -160,11 +166,7 @@ impl core::fmt::Display for DeJsonErr {
     }
 }
 
-#[cfg(feature = "no_std")]
-impl core::error::Error for DeJsonErr {}
-
-#[cfg(not(feature = "no_std"))]
-impl std::error::Error for DeJsonErr {}
+impl Error for DeJsonErr {}
 
 impl DeJsonState {
     pub fn next(&mut self, i: &mut Chars) {
@@ -878,7 +880,7 @@ where
     }
 }
 
-#[cfg(not(feature = "no_std"))]
+#[cfg(feature = "std")]
 impl<T> SerJson for std::collections::HashSet<T>
 where
     T: SerJson,
@@ -899,7 +901,7 @@ where
     }
 }
 
-#[cfg(not(feature = "no_std"))]
+#[cfg(feature = "std")]
 impl<T> DeJson for std::collections::HashSet<T>
 where
     T: DeJson + core::hash::Hash + Eq,
@@ -1172,7 +1174,7 @@ where
     }
 }
 
-#[cfg(not(feature = "no_std"))]
+#[cfg(feature = "std")]
 impl<K, V> SerJson for std::collections::HashMap<K, V>
 where
     K: SerJson,
@@ -1197,7 +1199,7 @@ where
     }
 }
 
-#[cfg(not(feature = "no_std"))]
+#[cfg(feature = "std")]
 impl<K, V> DeJson for std::collections::HashMap<K, V>
 where
     K: DeJson + Eq + core::hash::Hash,
diff --git a/src/serde_ron.rs b/src/serde_ron.rs
index 71077e7..dd96ca7 100644
--- a/src/serde_ron.rs
+++ b/src/serde_ron.rs
@@ -1,5 +1,11 @@
 use core::str::Chars;
 
+// remove this after 1.81 is live
+#[cfg(not(feature = "std"))]
+use core::error::Error;
+#[cfg(feature = "std")]
+use std::error::Error;
+
 use alloc::boxed::Box;
 use alloc::collections::{BTreeMap, BTreeSet, LinkedList};
 use alloc::format;
@@ -157,11 +163,7 @@ impl core::fmt::Display for DeRonErr {
     }
 }
 
-#[cfg(feature = "no_std")]
-impl core::error::Error for DeRonErr {}
-
-#[cfg(not(feature = "no_std"))]
-impl std::error::Error for DeRonErr {}
+impl Error for DeRonErr {}
 
 impl DeRonState {
     pub fn next(&mut self, i: &mut Chars) {
@@ -876,7 +878,7 @@ where
     }
 }
 
-#[cfg(not(feature = "no_std"))]
+#[cfg(feature = "std")]
 impl<T> SerRon for std::collections::HashSet<T>
 where
     T: SerRon,
@@ -897,7 +899,7 @@ where
     }
 }
 
-#[cfg(not(feature = "no_std"))]
+#[cfg(feature = "std")]
 impl<T> DeRon for std::collections::HashSet<T>
 where
     T: DeRon + core::hash::Hash + Eq,
@@ -1184,7 +1186,7 @@ where
     }
 }
 
-#[cfg(not(feature = "no_std"))]
+#[cfg(feature = "std")]
 impl<K, V> SerRon for std::collections::HashMap<K, V>
 where
     K: SerRon,
@@ -1204,7 +1206,7 @@ where
     }
 }
 
-#[cfg(not(feature = "no_std"))]
+#[cfg(feature = "std")]
 impl<K, V> DeRon for std::collections::HashMap<K, V>
 where
     K: DeRon + Eq + core::hash::Hash,
diff --git a/src/toml.rs b/src/toml.rs
index 3073e02..1bd09ac 100644
--- a/src/toml.rs
+++ b/src/toml.rs
@@ -1,5 +1,11 @@
 use core::str::Chars;
 
+// remove this after 1.81 is live
+#[cfg(not(feature = "std"))]
+use core::error::Error;
+#[cfg(feature = "std")]
+use std::error::Error;
+
 use alloc::format;
 use alloc::string::{String, ToString};
 use alloc::{collections::BTreeMap, vec, vec::Vec};
@@ -249,11 +255,7 @@ impl Out {
     }
 }
 
-#[cfg(feature = "no_std")]
-impl core::error::Error for TomlErr {}
-
-#[cfg(not(feature = "no_std"))]
-impl std::error::Error for TomlErr {}
+impl Error for TomlErr {}
 
 impl TomlParser {
     /// Parse a TOML string.


Test patch:
diff --git a/tests/json.rs b/tests/json.rs
index dc160f8..a9a79e4 100644
--- a/tests/json.rs
+++ b/tests/json.rs
@@ -7,7 +7,7 @@ use std::{
     sync::atomic::AtomicBool,
 };
 
-#[cfg(not(feature = "no_std"))]
+#[cfg(feature = "std")]
 use std::collections::HashMap;
 
 #[test]
@@ -70,6 +70,7 @@ fn de_multiline_comment() {
 fn de_illegal_inline_comment() {
     #[derive(DeJson)]
     pub struct Test {
+        #[allow(unused)]
         pub a: f32,
     }
 
@@ -93,6 +94,7 @@ fn de_illegal_inline_comment() {
 fn de_illegal_multiline_comment() {
     #[derive(DeJson)]
     pub struct Test {
+        #[allow(unused)]
         pub a: f32,
     }
 
@@ -468,7 +470,7 @@ fn path_type() {
     assert_eq!(bar.d, Some(vec![vec![1, 2], vec![3, 4]]));
 }
 
-#[cfg(not(feature = "no_std"))]
+#[cfg(feature = "std")]
 #[test]
 fn hashmaps() {
     #[derive(DeJson)]
diff --git a/tests/ron.rs b/tests/ron.rs
index b87e462..67a6e10 100644
--- a/tests/ron.rs
+++ b/tests/ron.rs
@@ -6,7 +6,7 @@ use std::{
     sync::atomic::AtomicBool,
 };
 
-#[cfg(not(feature = "no_std"))]
+#[cfg(feature = "std")]
 use std::collections::HashMap;
 
 #[test]
@@ -263,7 +263,7 @@ fn path_type() {
     assert_eq!(bar.d, Some(vec![vec![1, 2], vec![3, 4]]));
 }
 
-#[cfg(not(feature = "no_std"))]
+#[cfg(feature = "std")]
 #[test]
 fn hashmaps() {
     #[derive(DeRon)]