rustructure 0.2.0

Run-time introspection on compile-time flagged structures.
Documentation
#![doc = include_str!("../README.md")]

#![cfg_attr(not(feature = "std"), no_std)]

pub use rustructure_macros::Walkable;

mod impls;

/// For `Walker::visit_integer`, the type of integer encountered.
#[allow(non_camel_case_types)]
pub enum IntegerType {
    u8,
    i8,
    u16,
    i16,
    u32,
    i32,
    u64,
    i64,
    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
/// should take place, it can be invoked at a place of your choosing via `W::walk_with(self)`.
pub trait Walker {
    fn visit_unit(&mut self);
    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);
    fn visit_skip_field(&mut self, field_name: &'static str);

    fn visit_enum<W: Walkable>(&mut self, enum_name: &'static str);
    fn visit_variant<W: Walkable>(&mut self, variant_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::{IntegerType, Walkable, Walker, StdType};

    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.is_empty() {
                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_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) {
            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_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 += ")";
        }

        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_skip_field(&mut self, field_name: &'static str) {
            self.buffer += format!(" (skip_field:{})", field_name).as_str();
        }

        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 += ")";
        }
    }
}