Browse Source

Add support for references/lifetimes, broader std support, and more docs.

Kestrel 2 years ago
parent
commit
653067f003
5 changed files with 235 additions and 7 deletions
  1. 32 1
      README.md
  2. 13 4
      rustructure-macros/src/lib.rs
  3. 70 0
      rustructure-test/src/main.rs
  4. 73 0
      rustructure/src/impls.rs
  5. 47 2
      rustructure/src/lib.rs

+ 32 - 1
README.md

@@ -7,4 +7,35 @@ deserialization as a complement to `serde` when a format requires foreknowledge
 of the type at a time it is impossible to have an actual instance of the type.
 
 This crate _can_ be `no_std` by removing the default `std` feature, though
-without `alloc` its usefulness is likely limited.
+without at least `alloc` its usefulness is likely limited until [generic
+associated types](https://github.com/rust-lang/rust/issues/44265) land in
+stable.
+
+The main features of this library are the `Walkable` trait/derive macro and
+`Walker` trait. A simple example `Walker` that produces an S-expression-like
+representation of a data type is included as `StringWalker` (unavailable in
+`no_std`):
+
+```rust
+use rustructure::Walkable;
+
+#[derive(Walkable)]
+struct ExampleStruct<'l> {
+    a: Option<u8>,
+    b: &'l str,
+    c: (u64, String)
+}
+
+assert_eq!(
+    rustructure::StringWalker::walk::<ExampleStruct>(),
+    "(struct:ExampleStruct \
+        (field:a (option integer)) \
+        (field:b str) \
+        (field:c (tuple \
+            (field:0 integer) \
+            (field:1 str)\
+            )\
+        )\
+    )".to_string()
+);
+```

+ 13 - 4
rustructure-macros/src/lib.rs

@@ -51,17 +51,26 @@ fn struct_walkable(input: &syn::DeriveInput, ds: &syn::DataStruct) -> TokenStrea
         syn::Fields::Unnamed(u) => fields_walkable(u.unnamed.iter()),
     };
 
+    let lifetimes = (&input).generics.lifetimes().collect::<Vec<_>>();
+
+    let lifetime_markers = input.generics.lifetimes().map(|x| {
+        let name = &x.lifetime.ident;
+        quote!{
+            #name: std::marker::PhantomData<&#x u8>
+        }
+    });
+
     quote! {
-        impl ::rustructure::Walkable for #name {
+        impl<#(#lifetimes),*> ::rustructure::Walkable for #name<#(#lifetimes),*> {
             fn walk_with<W: ::rustructure::Walker>(walker: &mut W) {
-                struct Fields {};
-                impl ::rustructure::Walkable for Fields {
+                struct Fields<#(#lifetimes),*> { #(#lifetime_markers),* };
+                impl<#(#lifetimes),*> ::rustructure::Walkable for Fields<#(#lifetimes),*> {
                     fn walk_with<W: ::rustructure::Walker>(walker: &mut W) {
                         #walk_fields
                     }
                 }
 
-                walker.visit_struct::<Fields>(#name_str);
+                walker.visit_struct::<Fields<#(#lifetimes),*>>(#name_str);
             }
         }
     }.into()

+ 70 - 0
rustructure-test/src/main.rs

@@ -2,6 +2,48 @@ fn main() {
     println!("Hello, world!");
 }
 
+#[cfg(test)]
+#[allow(dead_code)]
+mod atoms {
+    use rustructure::StringWalker;
+
+    #[test]
+    fn test_atoms() {
+        assert_eq!(
+            StringWalker::walk::<()>(),
+            "unit".to_owned()
+        );
+        assert_eq!(
+            StringWalker::walk::<u8>(),
+            "integer".to_owned()
+        );
+        assert_eq!(
+            StringWalker::walk::<&'static str>(),
+            "str".to_owned()
+        );
+        assert_eq!(
+            StringWalker::walk::<String>(),
+            "str".to_owned()
+        );
+        assert_eq!(
+            StringWalker::walk::<Option<u8>>(),
+            "(option integer)".to_owned()
+        );
+        assert_eq!(
+            StringWalker::walk::<Vec<u8>>(),
+            "(vec integer)".to_owned()
+        );
+        assert_eq!(
+            StringWalker::walk::<[u8;0]>(),
+            "(array:0 integer)".to_owned()
+        );
+        assert_eq!(
+            StringWalker::walk::<&'static [u8]>(),
+            "(slice integer)".to_owned()
+        );
+    }
+}
+
 #[cfg(test)]
 #[allow(dead_code)]
 mod unnested {
@@ -188,3 +230,31 @@ mod skips {
         )
     }
 }
+
+#[cfg(test)]
+#[allow(dead_code)]
+mod lifetimes {
+    use rustructure::{StringWalker, Walkable};
+
+    #[derive(Walkable)]
+    struct SimpleReference<'l> {
+        a: &'l u8
+    }
+
+    #[derive(Walkable)]
+    struct SimpleMutReference<'l> {
+        a: &'l mut u8
+    }
+
+    #[test]
+    fn test_simple_references() {
+        assert_eq!(
+            StringWalker::walk::<SimpleReference<'_>>(),
+            "(struct:SimpleReference (field:a (ref integer)))".to_owned()
+        );
+        assert_eq!(
+            StringWalker::walk::<SimpleMutReference<'_>>(),
+            "(struct:SimpleMutReference (field:a (ref integer)))".to_owned()
+        )
+    }
+}

+ 73 - 0
rustructure/src/impls.rs

@@ -1,5 +1,17 @@
 use crate::{Walkable, Walker};
 
+impl<'a, T: Walkable> Walkable for &'a T {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        walker.visit_ref::<T>()
+    }
+}
+
+impl<'a, T: Walkable> Walkable for &'a mut T {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        walker.visit_ref::<T>()
+    }
+}
+
 impl Walkable for u8 {
     fn walk_with<W: Walker>(walker: &mut W) {
         walker.visit_integer(crate::IntegerType::u8)
@@ -54,6 +66,12 @@ impl Walkable for usize {
     }
 }
 
+impl<'l> Walkable for &'l str {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        walker.visit_str()
+    }
+}
+
 impl<T: Walkable> Walkable for Option<T> {
     fn walk_with<W: Walker>(walker: &mut W) {
         walker.visit_option::<T>()
@@ -127,3 +145,58 @@ impl<T0: Walkable, T1: Walkable, T2: Walkable, T3: Walkable> Walkable for (T0, T
         walker.visit_tuple::<Tuple<(T0, T1, T2, T3)>>();
     }
 }
+
+impl<T: Walkable, const LEN: usize> Walkable for [T;LEN] {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        walker.visit_array::<T>(LEN);
+    }
+}
+
+impl<'l, T: Walkable> Walkable for &'l [T] {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        walker.visit_slice::<T>();
+    }
+}
+
+#[cfg(feature = "std")]
+mod std_types {
+    use crate::StdType;
+
+    use super::{Walker,Walkable};
+
+    impl Walkable for String {
+        fn walk_with<W: Walker>(walker: &mut W) {
+            walker.visit_str()
+        }
+    }
+
+    impl<T: Walkable> Walkable for Vec<T> {
+        fn walk_with<W: Walker>(walker: &mut W) {
+            walker.visit_vec::<T>()
+        }
+    }
+
+    impl<T: Walkable> Walkable for std::rc::Rc<T> {
+        fn walk_with<W: Walker>(walker: &mut W) {
+            walker.visit_std_type::<T>(StdType::Rc)
+        }
+    }
+
+    impl<T: Walkable> Walkable for std::sync::Arc<T> {
+        fn walk_with<W: Walker>(walker: &mut W) {
+            walker.visit_std_type::<T>(StdType::Arc)
+        }
+    }
+
+    impl<T: Walkable> Walkable for std::cell::Cell<T> {
+        fn walk_with<W: Walker>(walker: &mut W) {
+            walker.visit_std_type::<T>(StdType::Cell)
+        }
+    }
+
+    impl<T: Walkable> Walkable for std::cell::RefCell<T> {
+        fn walk_with<W: Walker>(walker: &mut W) {
+            walker.visit_std_type::<T>(StdType::RefCell)
+        }
+    }
+}

+ 47 - 2
rustructure/src/lib.rs

@@ -20,6 +20,18 @@ pub enum IntegerType {
     usize,
 }
 
+/// For `Walker::visit_std_type`, the std type encountered.
+#[cfg(feature = "std")]
+#[non_exhaustive]
+#[derive(Debug)]
+pub enum StdType {
+    Cell,
+    RefCell,
+    Rc,
+    Arc,
+
+}
+
 /// User-facing type.
 ///
 /// In the non-leaf functions (those that take a `Walkable` generic), if recursion
@@ -29,9 +41,16 @@ pub trait Walker {
     fn visit_integer(&mut self, itype: IntegerType);
     fn visit_str(&mut self);
 
+    fn visit_ref<W: Walkable>(&mut self);
     fn visit_option<W: Walkable>(&mut self);
     fn visit_tuple<W: Walkable>(&mut self);
     fn visit_array<W: Walkable>(&mut self, length: usize);
+    fn visit_slice<W: Walkable>(&mut self);
+
+    #[cfg(feature = "std")]
+    fn visit_vec<W: Walkable>(&mut self);
+    #[cfg(feature = "std")]
+    fn visit_std_type<W: Walkable>(&mut self, std: StdType);
 
     fn visit_struct<W: Walkable>(&mut self, struct_name: &'static str);
     fn visit_field<W: Walkable>(&mut self, field_name: &'static str);
@@ -50,7 +69,7 @@ pub use sw::StringWalker;
 
 #[cfg(feature = "std")]
 mod sw {
-    use crate::{IntegerType, Walkable, Walker};
+    use crate::{IntegerType, Walkable, Walker, StdType};
 
     pub struct StringWalker {
         buffer: String,
@@ -83,8 +102,16 @@ mod sw {
             self.buffer += " str";
         }
 
+        fn visit_ref<W: Walkable>(&mut self) {
+            self.buffer += " (ref";
+            W::walk_with(self);
+            self.buffer += ")";
+        }
+
         fn visit_option<W: Walkable>(&mut self) {
             self.buffer += " (option";
+            W::walk_with(self);
+            self.buffer += ")";
         }
 
         fn visit_tuple<W: Walkable>(&mut self) {
@@ -94,7 +121,25 @@ mod sw {
         }
 
         fn visit_array<W: Walkable>(&mut self, length: usize) {
-            self.buffer += format!(" (array:{} ", length).as_str();
+            self.buffer += format!(" (array:{}", length).as_str();
+            W::walk_with(self);
+            self.buffer += ")";
+        }
+
+        fn visit_slice<W: Walkable>(&mut self) {
+            self.buffer += format!(" (slice").as_str();
+            W::walk_with(self);
+            self.buffer += ")";
+        }
+
+        fn visit_vec<W: Walkable>(&mut self) {
+            self.buffer += format!(" (vec").as_str();
+            W::walk_with(self);
+            self.buffer += ")";
+        }
+
+        fn visit_std_type<W: Walkable>(&mut self, std: StdType) {
+            self.buffer += format!(" (std:{:?}", std).as_str();
             W::walk_with(self);
             self.buffer += ")";
         }