Browse Source

Initial working version.

Kestrel 2 years ago
commit
9405467ae2

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+/target
+.*.sw?

+ 1 - 0
.vimrc

@@ -0,0 +1 @@
+set wildignore+=target,archive

+ 61 - 0
Cargo.lock

@@ -0,0 +1,61 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rustructure"
+version = "0.1.0"
+dependencies = [
+ "rustructure-macros",
+]
+
+[[package]]
+name = "rustructure-macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "rustructure-test"
+version = "0.1.0"
+dependencies = [
+ "rustructure",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"

+ 2 - 0
Cargo.toml

@@ -0,0 +1,2 @@
+[workspace]
+members = ["rustructure", "rustructure-macros", "rustructure-test"]

+ 2 - 0
rustructure-macros/.gitignore

@@ -0,0 +1,2 @@
+/target
+Cargo.lock

+ 14 - 0
rustructure-macros/Cargo.toml

@@ -0,0 +1,14 @@
+[package]
+name = "rustructure-macros"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[lib]
+proc-macro = true
+
+[dependencies]
+syn = { version = "1.0", features = ["derive", "extra-traits"] }
+proc-macro2 = "1.0"
+quote = "1.0"

+ 109 - 0
rustructure-macros/src/lib.rs

@@ -0,0 +1,109 @@
+use proc_macro::{TokenStream};
+use quote::{quote};
+
+#[proc_macro_derive(Walkable)]
+pub fn walkable(tokens: TokenStream) -> TokenStream {
+    let input = syn::parse_macro_input!(tokens as syn::DeriveInput);
+
+    if let syn::Data::Struct(s) = &input.data {
+        struct_walkable(&input, s)
+    }
+    else if let syn::Data::Enum(e) = &input.data {
+        enum_walkable(&input, e)
+    }
+    else {
+        todo!("Unions currently not implemented")
+    }
+}
+
+fn fields_walkable<'i, I: Iterator<Item = &'i syn::Field>>(input: I) -> proc_macro2::TokenStream {
+    let mut fields = Vec::new();
+    let mut index = 0;
+    for field in input {
+
+        let ident =
+            if let Some(i) = &field.ident {
+                i.to_string()
+            }
+            else {
+                index += 1;
+                format!("{}", index-1)
+            };
+        
+
+        let ty = &field.ty;
+        fields.push(quote!{ walker.visit_field::<#ty>(#ident) })
+    }
+
+    quote!{ #(#fields);* }
+}
+
+fn struct_walkable(input: &syn::DeriveInput, ds: &syn::DataStruct) -> TokenStream {
+    let name = &input.ident;
+    let name_str = input.ident.to_string();
+
+    let walk_fields = match &ds.fields {
+        syn::Fields::Unit => quote!{},
+        syn::Fields::Named(u) => fields_walkable(u.named.iter()),
+        syn::Fields::Unnamed(u) => fields_walkable(u.unnamed.iter()),
+    };
+
+    let ret = quote!{
+        impl ::rustructure::Walkable for #name {
+            fn walk_with<W: ::rustructure::Walker>(walker: &mut W) {
+                struct Fields {};
+                impl ::rustructure::Walkable for Fields {
+                    fn walk_with<W: ::rustructure::Walker>(walker: &mut W) {
+                        #walk_fields
+                    }
+                }
+
+                walker.visit_struct::<Fields>(#name_str);
+            }
+        }
+    }.into();
+
+    ret
+}
+
+fn enum_walkable(input: &syn::DeriveInput, de: &syn::DataEnum) -> TokenStream {
+    let name = &input.ident;
+    let name_str = input.ident.to_string();
+
+    let mut walk_variants = Vec::new();
+
+    for variant in &de.variants {
+        let variant_name = &variant.ident;
+        let variant_name_str = variant_name.to_string();
+
+        let visit_fields = fields_walkable(variant.fields.iter());
+
+        walk_variants.push(quote!{
+            #[allow(non_camel_case_types)]
+            struct #variant_name {};
+            impl ::rustructure::Walkable for #variant_name {
+                fn walk_with<W: ::rustructure::Walker>(walker: &mut W) {
+                    #visit_fields
+                }
+            }
+
+            walker.visit_variant::<#variant_name>(#variant_name_str);
+        });
+    }
+
+    quote!{
+        impl ::rustructure::Walkable for #name {
+            fn walk_with<W: ::rustructure::Walker>(walker: &mut W) {
+
+                struct WalkEnum {};
+                impl ::rustructure::Walkable for WalkEnum {
+                    fn walk_with<W: ::rustructure::Walker>(walker: &mut W) {
+                        #(#walk_variants);*
+                    }
+                }
+
+                walker.visit_enum::<WalkEnum>(#name_str);
+            }
+        }
+    }.into()
+}

+ 1 - 0
rustructure-test/.gitignore

@@ -0,0 +1 @@
+/target

+ 9 - 0
rustructure-test/Cargo.toml

@@ -0,0 +1,9 @@
+[package]
+name = "rustructure-test"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+rustructure = { path = "../rustructure", version = "0.1.0", features = ["std"] }

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

@@ -0,0 +1,169 @@
+fn main() {
+    println!("Hello, world!");
+}
+
+#[cfg(test)]
+#[allow(dead_code)]
+mod unnested {
+    use rustructure::{Walkable, StringWalker};
+
+    #[derive(Walkable)]
+    struct EmptyStruct {}
+
+    #[derive(Walkable)]
+    struct SingletonStruct {
+        value: u8
+    }
+
+    #[derive(Walkable)]
+    struct DualStruct {
+        value: u8,
+        second_value: u8,
+    }
+
+    #[derive(Walkable)]
+    struct SingletonNewtypeStruct(u8);
+
+    #[derive(Walkable)]
+    struct DualNewtypeStruct(u8, u8);
+
+    #[derive(Walkable)]
+    enum UnitEnum {}
+
+    #[derive(Walkable)]
+    enum SingleVariantEnum {
+        A
+    }
+
+    #[derive(Walkable)]
+    enum DoubleVariantEnum {
+        A,
+        B
+    }
+
+    #[test]
+    fn struct_checks() {
+        assert_eq!(
+            StringWalker::walk::<EmptyStruct>(),
+            "(struct:EmptyStruct)".to_owned()
+        );
+
+        assert_eq!(
+            StringWalker::walk::<SingletonStruct>(),
+            "(struct:SingletonStruct (field:value integer))".to_owned()
+        );
+
+        assert_eq!(
+            StringWalker::walk::<DualStruct>(),
+            "(struct:DualStruct (field:value integer) (field:second_value integer))".to_owned()
+        );
+
+        assert_eq!(
+            StringWalker::walk::<SingletonNewtypeStruct>(),
+            "(struct:SingletonNewtypeStruct (field:0 integer))".to_owned()
+        );
+
+        assert_eq!(
+            StringWalker::walk::<DualNewtypeStruct>(),
+            "(struct:DualNewtypeStruct (field:0 integer) (field:1 integer))".to_owned()
+        );
+    }
+
+    #[test]
+    fn enum_checks() {
+        assert_eq!(
+            StringWalker::walk::<UnitEnum>(),
+            "(enum:UnitEnum)".to_owned()
+        );
+
+        assert_eq!(
+            StringWalker::walk::<SingleVariantEnum>(),
+            "(enum:SingleVariantEnum (variant:A))".to_owned()
+        );
+
+        assert_eq!(
+            StringWalker::walk::<DoubleVariantEnum>(),
+            "(enum:DoubleVariantEnum (variant:A) (variant:B))".to_owned()
+        );
+    }
+}
+
+#[cfg(test)]
+#[allow(dead_code)]
+mod simple_nested {
+    use rustructure::{Walkable, StringWalker};
+
+    #[derive(Walkable)]
+    struct Contained {
+        v: u8
+    }
+
+    #[derive(Walkable)]
+    struct Container {
+        c: Contained
+    }
+
+    #[test]
+    fn simple_container() {
+        assert_eq!(
+            StringWalker::walk::<Contained>(),
+            "(struct:Contained (field:v integer))".to_owned()
+        );
+
+        assert_eq!(
+            StringWalker::walk::<Container>(),
+            "(struct:Container (field:c (struct:Contained (field:v integer))))".to_owned()
+        );
+    }
+
+    #[derive(Walkable)]
+    enum IntegerHolder {
+        Hold8(u8)
+    }
+
+    #[derive(Walkable)]
+    enum StructHolder {
+        HoldContained(Contained)
+    }
+
+    #[test]
+    fn nonunit_variant() {
+        assert_eq!(
+            StringWalker::walk::<IntegerHolder>(),
+            "(enum:IntegerHolder (variant:Hold8 (field:0 integer)))".to_owned()
+        );
+
+        assert_eq!(
+            StringWalker::walk::<StructHolder>(),
+            "(enum:StructHolder (variant:HoldContained (field:0 (struct:Contained (field:v integer)))))".to_owned()
+        );
+    }
+
+    #[test]
+    fn simple_tuples() {
+        assert_eq!(
+            StringWalker::walk::<(u8,)>(),
+            "(tuple (field:0 integer))".to_owned()
+        );
+
+        assert_eq!(
+            StringWalker::walk::<(u8,u8)>(),
+            "(tuple (field:0 integer) (field:1 integer))".to_owned()
+        );
+
+        assert_eq!(
+            StringWalker::walk::<(u8,u8,u8)>(),
+            "(tuple (field:0 integer) (field:1 integer) (field:2 integer))".to_owned()
+        );
+
+        assert_eq!(
+            StringWalker::walk::<(u8,u8,u8,u8)>(),
+            "(tuple (field:0 integer) (field:1 integer) (field:2 integer) (field:3 integer))".to_owned()
+        );
+
+        assert_eq!(
+            StringWalker::walk::<((u8,),)>(),
+            "(tuple (field:0 (tuple (field:0 integer))))".to_owned()
+        );
+    }
+}

+ 2 - 0
rustructure/.gitignore

@@ -0,0 +1,2 @@
+/target
+Cargo.lock

+ 14 - 0
rustructure/Cargo.toml

@@ -0,0 +1,14 @@
+[package]
+name = "rustructure"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[features]
+default = ["std"]
+std = []
+
+[dependencies]
+rustructure-macros = { path = "../rustructure-macros", version = "0.1.0" }
+

+ 121 - 0
rustructure/src/impls.rs

@@ -0,0 +1,121 @@
+use crate::{Walkable,Walker};
+
+impl Walkable for u8 {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        walker.visit_integer(crate::IntegerType::u8)
+    }
+}
+
+impl Walkable for i8 {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        walker.visit_integer(crate::IntegerType::i8)
+    }
+}
+
+impl Walkable for u16 {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        walker.visit_integer(crate::IntegerType::u16)
+    }
+}
+
+impl Walkable for i16 {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        walker.visit_integer(crate::IntegerType::i16)
+    }
+}
+
+impl Walkable for u32 {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        walker.visit_integer(crate::IntegerType::u32)
+    }
+}
+
+impl Walkable for i32 {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        walker.visit_integer(crate::IntegerType::i32)
+    }
+}
+
+impl Walkable for u64 {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        walker.visit_integer(crate::IntegerType::u64)
+    }
+}
+
+impl Walkable for i64 {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        walker.visit_integer(crate::IntegerType::i64)
+    }
+}
+
+impl Walkable for usize {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        walker.visit_integer(crate::IntegerType::usize)
+    }
+}
+
+impl<T: Walkable> Walkable for Option<T> {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        walker.visit_option::<T>()
+    }
+}
+
+impl Walkable for () {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        walker.visit_unit()
+    }
+}
+
+impl<T0: Walkable> Walkable for (T0,) {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        struct Tuple<T> { pd: core::marker::PhantomData<T> }
+        impl<T0: Walkable> Walkable for Tuple<(T0,)> {
+            fn walk_with<W: Walker>(walker: &mut W) {
+                walker.visit_field::<T0>("0");
+            }
+        }
+        walker.visit_tuple::<Tuple<(T0,)>>();
+    }
+}
+
+impl<T0: Walkable, T1: Walkable> Walkable for (T0,T1) {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        struct Tuple<T> { pd: core::marker::PhantomData<T> }
+        impl<T0: Walkable, T1: Walkable> Walkable for Tuple<(T0,T1)> {
+            fn walk_with<W: Walker>(walker: &mut W) {
+                walker.visit_field::<T0>("0");
+                walker.visit_field::<T1>("1");
+            }
+        }
+        walker.visit_tuple::<Tuple<(T0,T1)>>();
+    }
+}
+
+impl<T0: Walkable, T1: Walkable, T2: Walkable> Walkable for (T0,T1,T2) {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        struct Tuple<T> { pd: core::marker::PhantomData<T> }
+        impl<T0: Walkable, T1: Walkable, T2: Walkable> Walkable for Tuple<(T0,T1,T2)> {
+            fn walk_with<W: Walker>(walker: &mut W) {
+                walker.visit_field::<T0>("0");
+                walker.visit_field::<T1>("1");
+                walker.visit_field::<T2>("2");
+            }
+        }
+        walker.visit_tuple::<Tuple<(T0,T1,T2)>>();
+    }
+}
+
+impl<T0: Walkable, T1: Walkable, T2: Walkable, T3: Walkable> Walkable for (T0,T1,T2,T3) {
+    fn walk_with<W: Walker>(walker: &mut W) {
+        struct Tuple<T> { pd: core::marker::PhantomData<T> }
+        impl<T0: Walkable, T1: Walkable, T2: Walkable, T3: Walkable> Walkable for Tuple<(T0,T1,T2,T3)> {
+            fn walk_with<W: Walker>(walker: &mut W) {
+                walker.visit_field::<T0>("0");
+                walker.visit_field::<T1>("1");
+                walker.visit_field::<T2>("2");
+                walker.visit_field::<T3>("3");
+            }
+        }
+        walker.visit_tuple::<Tuple<(T0,T1,T2,T3)>>();
+    }
+}

+ 117 - 0
rustructure/src/lib.rs

@@ -0,0 +1,117 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub use rustructure_macros::Walkable;
+
+mod impls;
+
+#[allow(non_camel_case_types)]
+pub enum IntegerType {
+    u8,
+    i8,
+    u16,
+    i16,
+    u32,
+    i32,
+    u64,
+    i64,
+    usize,
+}
+
+pub trait Walker {
+    fn visit_unit(&mut self);
+    fn visit_integer(&mut self, itype: IntegerType);
+    fn visit_str(&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_struct<W: Walkable>(&mut self, struct_name: &'static str);
+    fn visit_field<W: Walkable>(&mut self, field_name: &'static str);
+
+    fn visit_variant<W: Walkable>(&mut self, variant_name: &'static str);
+
+    fn visit_enum<W: Walkable>(&mut self, enum_name: &'static str);
+}
+
+pub trait Walkable {
+    fn walk_with<W: Walker>(walker: &mut W);
+}
+
+#[cfg(feature = "std")]
+pub use sw::StringWalker;
+
+#[cfg(feature = "std")]
+mod sw {
+    use crate::{Walker,Walkable,IntegerType};
+
+    pub struct StringWalker {
+        buffer: String
+    }
+
+    impl StringWalker {
+        pub fn walk<T: Walkable>() -> String {
+            let mut sw = Self { buffer: String::new() };
+            T::walk_with(&mut sw);
+            // remove inevitable leading space
+            if sw.buffer.len() > 0 {
+                sw.buffer.remove(0);
+            }
+            sw.buffer
+        }
+    }
+
+    impl Walker for StringWalker {
+        fn visit_unit(&mut self) {
+            self.buffer += " unit";
+        }
+
+        fn visit_integer(&mut self, _itype: IntegerType) {
+            self.buffer += " integer";
+        }
+
+        fn visit_str(&mut self) {
+            self.buffer += " str";
+        }
+
+        fn visit_option<W: Walkable>(&mut self) {
+            self.buffer += " (option";
+        }
+
+        fn visit_tuple<W: Walkable>(&mut self) {
+            self.buffer += " (tuple";
+            W::walk_with(self);
+            self.buffer += ")";
+        }
+
+        fn visit_array<W: Walkable>(&mut self, length: usize) {
+            self.buffer += format!(" (array:{} ", length).as_str();
+            W::walk_with(self);
+            self.buffer += ")";
+        }
+
+        fn visit_field<W: Walkable>(&mut self, field_name: &'static str) {
+            self.buffer += format!(" (field:{}", field_name).as_str();
+            W::walk_with(self);
+            self.buffer += ")";
+        }
+
+        fn visit_variant<W: Walkable>(&mut self, variant_name: &'static str) {
+            self.buffer += format!(" (variant:{}", variant_name).as_str();
+            W::walk_with(self);
+            self.buffer += ")";
+        }
+
+        fn visit_struct<W: Walkable>(&mut self, struct_name: &'static str) {
+            self.buffer += format!(" (struct:{}", struct_name).as_str();
+            W::walk_with(self);
+            self.buffer += ")";
+        }
+
+        fn visit_enum<W: Walkable>(&mut self, enum_name: &'static str) {
+            self.buffer += format!(" (enum:{}", enum_name).as_str();
+            W::walk_with(self);
+            self.buffer += ")";
+        }
+    }
+}