From cf463408e2c048299ba48137bac1a9e12d8c4d04 Mon Sep 17 00:00:00 2001
From: Victor Dudochkin <dudochkin.victor@gmail.com>
Date: Thu, 9 Mar 2023 13:43:17 +0700
Subject: [PATCH 01/13] Massive update

---
 Cargo.toml                                    |  18 +-
 build.rs                                      |   8 +
 macro/Cargo.toml                              |   6 +-
 macro/examples/aop.rs                         |  58 --
 macro/src/impls/compose.rs                    | 186 ++++
 macro/src/impls/contract_aspect.rs            | 958 ++++++++++++++++++
 macro/src/impls/decorate.rs                   | 109 ++
 macro/src/impls/delegate.rs                   | 213 ++++
 macro/src/impls/mock.rs                       | 217 ++++
 macro/src/impls/mod.rs                        | 225 ++++
 macro/src/impls/register.rs                   | 459 +++++++++
 macro/src/impls/utils.rs                      |  49 +
 macro/src/lib.rs                              | 454 +++++++--
 src/foundation/patterns/default/controller.rs |   4 +-
 src/foundation/patterns/default/model.rs      |   3 +
 src/foundation/patterns/default/view.rs       | 101 +-
 src/foundation/patterns/fsm/integrations.rs   |  10 +-
 src/foundation/patterns/mediator/mediator.rs  |   2 +-
 src/foundation/patterns/mod.rs                |   8 +-
 src/foundation/patterns/observer/notifier.rs  |   1 +
 src/lib.rs                                    |  23 +-
 src/prelude/compose.rs                        |  28 +
 src/prelude/currying.rs                       | 126 +++
 src/prelude/mod.rs                            |  26 +
 src/prelude/pipe.rs                           | 470 +++++++++
 src/utils/mod.rs                              |  28 +
 src/utils/panic/handler.rs                    | 142 +++
 src/utils/panic/mod.rs                        |  17 +
 src/utils/panic/provider.rs                   |  63 ++
 src/utils/panic/validate.rs                   |  20 +
 src/utils/terminal/mod.rs                     | 100 ++
 31 files changed, 3947 insertions(+), 185 deletions(-)
 create mode 100644 build.rs
 delete mode 100644 macro/examples/aop.rs
 create mode 100644 macro/src/impls/compose.rs
 create mode 100644 macro/src/impls/contract_aspect.rs
 create mode 100644 macro/src/impls/decorate.rs
 create mode 100644 macro/src/impls/delegate.rs
 create mode 100644 macro/src/impls/mock.rs
 create mode 100644 macro/src/impls/mod.rs
 create mode 100644 macro/src/impls/register.rs
 create mode 100644 macro/src/impls/utils.rs
 create mode 100644 src/prelude/compose.rs
 create mode 100644 src/prelude/currying.rs
 create mode 100644 src/prelude/pipe.rs
 create mode 100644 src/utils/mod.rs
 create mode 100644 src/utils/panic/handler.rs
 create mode 100644 src/utils/panic/mod.rs
 create mode 100644 src/utils/panic/provider.rs
 create mode 100644 src/utils/panic/validate.rs
 create mode 100644 src/utils/terminal/mod.rs

diff --git a/Cargo.toml b/Cargo.toml
index 7bb2fed..a0e79c8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,4 +18,20 @@ maintenance = { status = "actively-developed" }
 [dependencies]
 log = "0.4"
 serde = { version = "1.0", features = ["derive"] }
-once_cell = "1.7.2"
+once_cell = "1.7"
+futures = "0.3"
+ruex-macro = { version="0", path = "macro" }
+colored = "2.0"
+atty = "0.2"
+url = "2.2"
+urlencoding = "2.1"
+color-backtrace = "0.5"
+fern = { version = "0.6", features = ["colored", "syslog-6", "meta-logging-in-format", "chrono"] }
+humantime = "2.1"
+syslog = "6"
+# contracts = "0.6"
+# systemd-journal-logger = "0.7"
+rust-companion = "0"
+
+[build-dependencies]
+rust-companion = "0"
\ No newline at end of file
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..63c5e4f
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,8 @@
+fn main() {
+    match companion::bootstrap() {
+        Ok(lock) => {
+            println!("cargo:rerun-if-changed={:?}", lock);
+        }
+        Err(_) => println!("already launched"),
+    }
+}
diff --git a/macro/Cargo.toml b/macro/Cargo.toml
index 751cf67..eecbd91 100644
--- a/macro/Cargo.toml
+++ b/macro/Cargo.toml
@@ -9,6 +9,10 @@ proc-macro = true
 
 [dependencies]
 proc-macro2 = "1.0"
-syn = { version = "1.0", features = ["full"]}
+syn = { version = "1.0", features = ["full", "fold", "extra-traits", "visit", "visit-mut"] }
 quote = "1.0"
 proc-macro-error = "1.0"
+serde_tokenstream = "0.1"
+syn-serde = { version = "0.2", features = ["json"] }
+serde = { version = "1", features = ["derive"] }
+rust-companion = "0"
\ No newline at end of file
diff --git a/macro/examples/aop.rs b/macro/examples/aop.rs
deleted file mode 100644
index c5d46e6..0000000
--- a/macro/examples/aop.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-use ruex_macro::*;
-
-// #[Aspect {
-//     advice: "aspects::TestAspect",
-//     before: "before(val,val2)", 
-//     after: "after()"
-// }]
-// fn test_aop(&self, val: i32) -> Result<String, AppErr> {
-//     let joint_point = move || {
-//         println!("Closure {:?} with {}", self, val)
-//     };
-
-//     joint_point();
-
-//     Ok(String::from("Good"))
-// }
-
-#[derive(Debug)]
-enum AppErr {
-    WrongParam,
-}
-
-mod aspects {
-    use super::AppErr;
-
-    struct TestAspect;
-
-    impl TestAspect {
-        fn before(val: i32) -> Result<(), AppErr> {
-            println!("called before");
-            Ok(())
-        }
-
-        fn after(val: Result<String, AppErr>) -> Result<String, AppErr> {
-            println!("called after");
-            Ok(String::from("Here"))
-        }
-    }
-}
-
-#[derive(Debug)]
-struct AopExample;
-
-impl AopExample {
-    #[Aspect {
-        advice: "aspects::TestAspect",
-        before: "before(val)", 
-        after: "after()"
-    }]
-    fn test_aop(&self, val: i32) -> Result<String, AppErr> {
-        Ok(String::from("Good"))
-    }
-}
-
-fn main() {
-    let ex = AopExample;
-    println!("HERE {:?}", ex.test_aop(10));
-}
\ No newline at end of file
diff --git a/macro/src/impls/compose.rs b/macro/src/impls/compose.rs
new file mode 100644
index 0000000..9e6628a
--- /dev/null
+++ b/macro/src/impls/compose.rs
@@ -0,0 +1,186 @@
+use std::net::UdpSocket;
+
+// use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+use syn::{
+    parse::{Parse, ParseStream, Result},
+    punctuated::Punctuated,
+    Data, DeriveInput, Field, Fields, Ident, Path, Token, TraitItem,
+};
+
+use companion::{companion_addr, Response, Task};
+
+use crate::MAX_UDP_PAYLOAD;
+
+#[derive(Default, Debug)]
+struct DelegateArgs {
+    paths: Vec<Path>,
+}
+
+impl Parse for DelegateArgs {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let items;
+        syn::parenthesized!(items in input);
+
+        let types: Punctuated<Path, Token![,]> = items.parse_terminated(syn::Path::parse).unwrap();
+        let paths = types.into_iter().collect::<Vec<_>>();
+
+        Ok(DelegateArgs { paths })
+    }
+}
+
+struct DelegateVar {
+    var: Ident,
+    args: Vec<DelegateArgs>,
+}
+
+fn filter_fields(field: &Field) -> Option<DelegateVar> {
+    if let Some(ident) = &field.ident {
+        let args: Vec<DelegateArgs> = field
+            .attrs
+            .iter()
+            .filter_map(|attr| {
+                let init = String::new();
+                let attribute = attr.path.segments.iter().fold(init, |acc, item| {
+                    if acc.is_empty() {
+                        format!("{}", item.ident)
+                    } else {
+                        format!("{acc}::{}", item.ident)
+                    }
+                });
+
+                if attribute == "delegate" {
+                    match syn::parse2::<DelegateArgs>(attr.tokens.clone()) {
+                        Ok(path) => Some(path),
+                        Err(_) => {
+                            panic!("usage: #[delegate(std::fmt::Display, Debug)]");
+                        }
+                    }
+                } else {
+                    None
+                }
+            })
+            .collect();
+        Some(DelegateVar {
+            var: ident.clone(),
+            args,
+        })
+    } else {
+        None
+    }
+}
+
+fn collect(data: &Data) -> Vec<DelegateVar> {
+    match &data {
+        Data::Struct(datastruct) => match &datastruct.fields {
+            Fields::Named(fields) => {
+                let collected: Vec<DelegateVar> =
+                    fields.named.iter().filter_map(filter_fields).collect();
+                collected
+            }
+            Fields::Unnamed(_) => todo!(),
+            Fields::Unit => todo!(),
+        },
+        Data::Enum(_) => todo!(),
+        Data::Union(_) => todo!(),
+    }
+}
+
+fn generate(var: &Ident, items: &Vec<TraitItem>) -> Vec<TokenStream2> {
+    items
+        .iter()
+        .filter_map(|item| {
+            if let TraitItem::Method(method) = item {
+                Some(&method.sig)
+            } else {
+                None
+            }
+        })
+        .map(|signature| {
+            let syn::Signature {
+                ident: sig, inputs, ..
+            } = &signature;
+            let inputs = inputs
+                .iter()
+                .filter_map(|arg| match arg {
+                    syn::FnArg::Receiver(_) => None,
+                    syn::FnArg::Typed(val) => {
+                        if let syn::Pat::Ident(pat) = val.pat.as_ref() {
+                            Some(pat.ident.clone())
+                        } else {
+                            None
+                        }
+                    }
+                })
+                .collect::<Vec<Ident>>();
+
+            quote! {
+                #signature {
+                    self.#var.#sig(#(#inputs), *)
+                }
+            }
+        })
+        .collect::<Vec<TokenStream2>>()
+}
+
+pub(crate) fn compose(item: TokenStream2) -> TokenStream2 {
+    let input: DeriveInput = syn::parse2(item.clone()).unwrap();
+
+    let DeriveInput { ident, data, .. } = input;
+
+    let collected = collect(&data);
+
+    let addr = companion_addr();
+
+    let socket = UdpSocket::bind("[::]:0").unwrap();
+    socket.connect(addr).unwrap();
+    let mut buf = [0; MAX_UDP_PAYLOAD];
+
+    let impls: Vec<TokenStream2> = collected
+        .iter()
+        .map(|item| {
+            let var = &item.var;
+            item.args
+                .iter()
+                .map(|item| {
+                    item.paths
+                        .iter()
+                        .map(|path| {
+                            let str_path = path
+                                .segments
+                                .iter()
+                                .map(|item| item.ident.to_string())
+                                .collect::<Vec<String>>()
+                                .join("::");
+
+                            socket.send(&Task::Get(&str_path).as_bytes()).unwrap();
+                            let (len, _src) = socket.recv_from(&mut buf).unwrap();
+                            let resp = Response::from(&buf[..len]);
+
+                            if let Response::String(data) = resp {
+                                let def: syn::ItemTrait = syn_serde::json::from_str(&data).unwrap();
+
+                                let syn::ItemTrait { items, .. } = def;
+                                let methods = generate(&var, &items);
+
+                                quote! {
+                                    impl #path for #ident {
+                                        #(#methods)*
+                                    }
+                                }
+                            } else {
+                                panic!("Trait `{}` is not registered", str_path)
+                            }
+                        })
+                        .collect::<Vec<TokenStream2>>()
+                })
+                .flatten()
+                .collect::<Vec<TokenStream2>>()
+        })
+        .flatten()
+        .collect();
+    quote! {
+        #(#impls)*
+    }
+}
diff --git a/macro/src/impls/contract_aspect.rs b/macro/src/impls/contract_aspect.rs
new file mode 100644
index 0000000..9186501
--- /dev/null
+++ b/macro/src/impls/contract_aspect.rs
@@ -0,0 +1,958 @@
+use std::{
+    collections::{HashMap, VecDeque},
+    fmt,
+    net::UdpSocket,
+};
+
+// use proc_macro::TokenStream;
+use proc_macro2::{Punct, Spacing, TokenStream as TokenStream2, TokenTree};
+use quote::{format_ident, quote};
+use syn::{
+    parse::{Parse, ParseStream, Result},
+    punctuated::Punctuated,
+    visit_mut::VisitMut,
+    Attribute, Expr, ExprLit, ExprPath, Lit, Meta, MetaNameValue, PathSegment, Token, TraitItem,
+    TraitItemMethod,
+};
+
+use companion::{companion_addr, Response, Task};
+
+use crate::MAX_UDP_PAYLOAD;
+
+pub struct AspectJointPoint<'a> {
+    pub stream: &'a TokenStream2,
+}
+
+impl<'a> VisitMut for AspectJointPoint<'a> {
+    fn visit_expr_mut(&mut self, item: &mut Expr) {
+        if let Expr::Call(ref node) = item {
+            if let Expr::Path(call) = node.func.as_ref() {
+                let segments = &call.path.segments;
+                if segments.len() == 2 {
+                    if let (Some(first), Some(last)) = (segments.first(), segments.last()) {
+                        if first.ident.to_string() == "AspectJointPoint"
+                            && last.ident.to_string() == "proceed"
+                        {
+                            // prepare variables
+                            let stream = self.stream;
+                            *item = syn::parse_quote!(#stream);
+                        }
+                    }
+                }
+            }
+        }
+
+        // Delegate to the default impl to visit nested expressions.
+        syn::visit_mut::visit_expr_mut(self, item);
+    }
+}
+
+#[derive(Default, Debug)]
+pub struct Aspect {
+    pub name: String,
+    pub docs: VecDeque<String>,
+    pub before: Option<TraitItemMethod>,
+    pub after: Option<TraitItemMethod>,
+    pub around: Option<TraitItemMethod>,
+}
+
+impl Aspect {
+    pub fn new(attr: TokenStream2) -> Self {
+        // prefetch from attrs
+        match syn::parse2::<syn::TypePath>(attr.clone()) {
+            Ok(_) => {}
+            Err(_) => panic!("Usage: #[aspect(std::fmt::Display)]"),
+        }
+
+        let key = attr.to_string().replace(" :: ", "::");
+
+        let addr = companion_addr();
+
+        let socket = UdpSocket::bind("[::]:0").unwrap();
+        socket.connect(addr).unwrap();
+        let mut buf = [0; MAX_UDP_PAYLOAD];
+
+        socket.send(&Task::Get(&key).as_bytes()).unwrap();
+        let (len, _src) = socket.recv_from(&mut buf).unwrap();
+        let resp = Response::from(&buf[..len]);
+
+        if let Response::String(data) = resp {
+            let item_trait: syn::ItemTrait = syn_serde::json::from_str(&data).unwrap();
+            let mut aspect = Aspect::default();
+            aspect.name = key;
+            aspect.docs = collect_docs(&item_trait.attrs);
+
+            // construct proto
+            for item in item_trait.items.iter() {
+                match item {
+                    TraitItem::Method(method) => {
+                        // here
+                        let name = method.sig.ident.to_string();
+                        // println!("Sig {}", name);
+                        match name.as_str() {
+                            "before" => aspect.before = Some(method.clone()),
+                            "after" => aspect.after = Some(method.clone()),
+                            "around" => aspect.around = Some(method.clone()),
+                            _ => {
+                                println!("Incompatible Aspect trait")
+                            }
+                        }
+                    }
+                    _ => {
+                        println!("Incompatible Aspect trait")
+                    }
+                }
+            }
+
+            aspect
+        } else {
+            panic!("Aspect is not registered")
+        }
+    }
+
+    pub fn documentation(&self) -> VecDeque<String> {
+        let mut output: VecDeque<String> = VecDeque::new();
+
+        let mut before_docs: VecDeque<String> = VecDeque::new();
+        let mut after_docs: VecDeque<String> = VecDeque::new();
+        let mut around_docs: VecDeque<String> = VecDeque::new();
+
+        if let Some(ref method) = self.before {
+            before_docs = collect_docs(&method.attrs);
+        }
+
+        if let Some(ref method) = self.around {
+            around_docs = collect_docs(&method.attrs);
+        }
+
+        if let Some(ref method) = self.after {
+            after_docs = collect_docs(&method.attrs);
+        }
+
+        // if there something in documentations
+        if !self.docs.is_empty()
+            || !before_docs.is_empty()
+            || !after_docs.is_empty()
+            || !around_docs.is_empty()
+        {
+            output.push_back(format!(" ### {}", self.name));
+            self.docs.iter().for_each(|item| {
+                output.push_back(item.clone());
+            });
+
+            if !before_docs.is_empty() {
+                // output.push_back(String::from(""));
+                output.push_back(String::from(" - __Before:__"));
+
+                before_docs.iter().for_each(|item| {
+                    output.push_back(item.clone());
+                });
+            }
+
+            if !around_docs.is_empty() {
+                // output.push_back(String::from(""));
+                output.push_back(String::from(" - __Around:__"));
+
+                around_docs.iter().for_each(|item| {
+                    output.push_back(item.clone());
+                });
+            }
+
+            if !after_docs.is_empty() {
+                // output.push_back(String::from(""));
+                output.push_back(String::from(" - __After:__"));
+
+                after_docs.iter().for_each(|item| {
+                    output.push_back(item.clone());
+                });
+            }
+        }
+
+        output
+    }
+}
+
+fn collect_docs(attrs: &Vec<Attribute>) -> VecDeque<String> {
+    let mut docs: VecDeque<String> = VecDeque::new();
+    attrs.iter().for_each(|attr| {
+        // Collect docs attributes
+        if attr.path.is_ident("doc") {
+            match attr.parse_meta().unwrap() {
+                Meta::NameValue(MetaNameValue {
+                    lit: Lit::Str(lit_str),
+                    ..
+                }) => {
+                    docs.push_back(lit_str.value());
+                }
+                _ => {}
+            }
+        }
+    });
+    docs
+}
+
+// detect ranges separated by implication and return
+fn stream_ranges(input: &Vec<TokenTree>) -> Vec<(usize, usize)> {
+    let mut ranges: Vec<(usize, usize)> = vec![];
+
+    let mut tail = &TokenTree::Punct(Punct::new(' ', Spacing::Alone));
+    let mut start_current = 0;
+
+    // split for  ->
+    for (idx, attr) in input.iter().enumerate() {
+        if let TokenTree::Punct(cur) = attr {
+            if cur.as_char() == '>' && cur.spacing() == Spacing::Alone {
+                if let TokenTree::Punct(prev) = tail {
+                    if prev.as_char() == '-' && prev.spacing() == Spacing::Joint {
+                        ranges.push((start_current, idx - 1));
+                        start_current = idx + 1;
+                    }
+                }
+            }
+        }
+
+        tail = attr;
+    }
+
+    if start_current < input.len() {
+        ranges.push((start_current, input.len()));
+    }
+
+    ranges
+}
+
+/// Checking-mode of a contract.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum Mode {
+    /// Always check contract
+    Always,
+    /// Never check contract
+    Disabled,
+    /// Check contract only in debug builds
+    Debug,
+    /// Check contract only in `#[cfg(test)]` configurations
+    Test,
+    /// Check the contract and print information upon violation, but don't abort
+    /// the program.
+    LogOnly,
+}
+
+impl Mode {
+    /// Return the prefix of attributes of `self` mode.
+    pub fn name(self) -> Option<&'static str> {
+        match self {
+            Mode::Always => Some(""),
+            Mode::Disabled => None,
+            Mode::Debug => Some("debug_"),
+            Mode::Test => Some("test_"),
+            Mode::LogOnly => None,
+        }
+    }
+
+    /// Computes the contract type based on feature flags.
+    pub fn final_mode(self) -> Self {
+        // disabled ones can't be "forced", test ones should stay test, no
+        // matter what.
+        if self == Mode::Disabled || self == Mode::Test {
+            return self;
+        }
+
+        if cfg!(feature = "disable_contracts") {
+            Mode::Disabled
+        } else if cfg!(feature = "override_debug") {
+            // log is "weaker" than debug, so keep log
+            if self == Mode::LogOnly {
+                self
+            } else {
+                Mode::Debug
+            }
+        } else if cfg!(feature = "override_log") {
+            Mode::LogOnly
+        } else {
+            self
+        }
+    }
+}
+
+/// The different contract types.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum Type {
+    Requires,
+    Ensures,
+    Invariant,
+    Aspect,
+}
+
+impl Type {
+    /// Get the name that is used as a message-prefix on violation of a
+    /// contract.
+    pub fn message_name(self) -> &'static str {
+        match self {
+            Type::Requires => "Pre-condition",
+            Type::Ensures => "Post-condition",
+            Type::Invariant => "Invariant",
+            Type::Aspect => "Invariant",
+        }
+    }
+
+    /// Determine the type and mode of an identifier.
+    pub fn type_and_mode(ident: &str) -> Option<(Type, Mode)> {
+        match ident {
+            "aspect" => Some((Type::Aspect, Mode::Always)),
+            "requires" => Some((Type::Requires, Mode::Always)),
+            "ensures" => Some((Type::Ensures, Mode::Always)),
+            "invariant" => Some((Type::Invariant, Mode::Always)),
+            "debug_requires" => Some((Type::Requires, Mode::Debug)),
+            "debug_ensures" => Some((Type::Ensures, Mode::Debug)),
+            "debug_invariant" => Some((Type::Invariant, Mode::Debug)),
+            "test_requires" => Some((Type::Requires, Mode::Test)),
+            "test_ensures" => Some((Type::Ensures, Mode::Test)),
+            "test_invariant" => Some((Type::Invariant, Mode::Test)),
+            _ => None,
+        }
+    }
+}
+
+// Contain single part of contract case as vector of Rule expressions
+#[derive(Debug)]
+pub struct CaseRule(Vec<RuleExpression>);
+
+impl CaseRule {
+    fn add(&mut self, value: RuleExpression) {
+        self.0.push(value);
+    }
+}
+
+impl Default for CaseRule {
+    fn default() -> Self {
+        Self(Default::default())
+    }
+}
+
+// Describe rule expression
+#[derive(Debug)]
+enum RuleExpression {
+    If(Expr),
+    Expr(Expr),
+    // Desc(String),
+}
+
+pub struct Contract {
+    pub ty: Type,
+    pub mode: Mode,
+    pub desc: Option<String>,
+    pub rules: Vec<CaseRule>,
+    // variable declarations
+    pub decls: HashMap<String, Expr>,
+}
+
+impl Contract {
+    pub fn syntax(&self) -> Vec<TokenStream2> {
+        let assert = match self.mode {
+            Mode::Always => format_ident!("assert"),
+            Mode::Debug | Mode::Test => {
+                format_ident!("debug_assert")
+            }
+            Mode::Disabled | Mode::LogOnly => return vec![],
+        };
+
+        self.rules
+            .iter()
+            .map(|rule| {
+                let mut holder = quote! {};
+
+                for expr in rule.0.iter().rev() {
+                    match expr {
+                        RuleExpression::If(e) => {
+                            holder = quote! {
+                                if (#e) {
+                                    #holder
+                                }
+                            };
+                        }
+                        RuleExpression::Expr(e) => match self.desc {
+                            Some(ref desc) => {
+                                holder = quote! {
+                                    #assert!(#e, #desc);
+                                };
+                            }
+                            None => {
+                                holder = quote! {
+                                    #assert!(#e);
+                                };
+                            }
+                        },
+                    }
+                }
+                holder
+            })
+            .collect()
+    }
+}
+
+impl fmt::Debug for Contract {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match &self.desc {
+            Some(desc) => {
+                write!(
+                    f,
+                    "{:?}:{:?}, {} rules [{desc}]",
+                    self.mode,
+                    self.ty,
+                    self.rules.len()
+                )
+            }
+            None => {
+                write!(
+                    f,
+                    "{:?}:{:?}, {} rules",
+                    self.mode,
+                    self.ty,
+                    self.rules.len()
+                )
+            }
+        }
+    }
+}
+
+impl Contract {
+    pub fn new(ty: Type, mode: Mode) -> Self {
+        Self {
+            ty,
+            mode,
+            desc: None,
+            rules: Vec::new(),
+            decls: HashMap::new(),
+        }
+    }
+
+    // Parse attribures from TokenStream into rules
+    //
+    pub fn parse_attributes(&mut self, stream: TokenStream2) {
+        // should split stream by implication first, instead coma
+        let stream = stream.into_iter().collect::<Vec<_>>();
+
+        let ranges = stream_ranges(&stream);
+
+        // `peekable` to look at the next element of the iterator without consuming it
+        let mut range_iter = ranges.iter().peekable();
+
+        // expressions placeholder
+        let mut rule = CaseRule::default();
+        while let Some(range) = range_iter.next() {
+            let stream = stream[range.0..range.1]
+                .iter()
+                .map(|x| x.clone())
+                .collect::<TokenStream2>();
+            let Segment { expressions } = syn::parse2(stream).unwrap();
+
+            let is_last_segment = range_iter.peek().is_none();
+
+            let mut expr_iter = expressions.iter().peekable();
+            while let Some(expr) = expr_iter.next() {
+                let is_last_expr = expr_iter.peek().is_none();
+
+                if !is_last_segment && is_last_expr {
+                    match expr {
+                        Expr::Lit(_) => panic!("Description should be last element"),
+                        _ => {
+                            let mut expr = expr.clone();
+                            let mut replacer = OldPseudo::default();
+                            replacer.visit_expr_mut(&mut expr);
+
+                            if self.ty != Type::Ensures && !replacer.items.is_empty() {
+                                panic!("Only ensures support 'old' pseudo-expressions")
+                            }
+
+                            replacer.items.into_iter().for_each(|(k, v)| {
+                                self.decls.insert(k, v);
+                            });
+
+                            rule.add(RuleExpression::If(expr));
+                        }
+                    }
+                } else if is_last_segment && !is_last_expr {
+                    match expr {
+                        Expr::Lit(_) => panic!("Description should be last element"),
+                        _ => {
+                            let mut expr = expr.clone();
+                            let mut replacer = OldPseudo::default();
+                            replacer.visit_expr_mut(&mut expr);
+
+                            if self.ty != Type::Ensures && !replacer.items.is_empty() {
+                                panic!("Only ensures support 'old' pseudo-expressions")
+                            }
+
+                            replacer.items.into_iter().for_each(|(k, v)| {
+                                self.decls.insert(k, v);
+                            });
+
+                            rule.add(RuleExpression::Expr(expr));
+                            self.rules.push(rule);
+                            rule = CaseRule::default();
+                        }
+                    }
+                } else if is_last_segment && is_last_expr {
+                    // get desc if exists
+                    match expr {
+                        Expr::Lit(ExprLit {
+                            lit: Lit::Str(token),
+                            ..
+                        }) => {
+                            self.desc = Some(token.value());
+                        }
+                        _ => {
+                            let mut expr = expr.clone();
+                            let mut replacer = OldPseudo::default();
+                            replacer.visit_expr_mut(&mut expr);
+
+                            if self.ty != Type::Ensures && !replacer.items.is_empty() {
+                                panic!("Only ensures support 'old' pseudo-expressions")
+                            }
+
+                            replacer.items.into_iter().for_each(|(k, v)| {
+                                self.decls.insert(k, v);
+                            });
+
+                            rule.add(RuleExpression::Expr(expr));
+                            self.rules.push(rule);
+                            rule = CaseRule::default();
+                        }
+                    }
+                } else {
+                    match expr {
+                        Expr::Lit(_) => panic!("Description should be last element"),
+                        _ => {
+                            let mut expr = expr.clone();
+                            let mut replacer = OldPseudo::default();
+                            replacer.visit_expr_mut(&mut expr);
+
+                            if self.ty != Type::Ensures && !replacer.items.is_empty() {
+                                panic!("Only ensures support 'old' pseudo-expressions")
+                            }
+
+                            replacer.items.into_iter().for_each(|(k, v)| {
+                                self.decls.insert(k, v);
+                            });
+
+                            rule.add(RuleExpression::Expr(expr));
+                            self.rules.push(rule);
+                            rule = CaseRule::default();
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+// faster trim whitespace
+pub fn trim_whitespace(s: &str) -> String {
+    let mut new_str = s.trim().to_owned();
+    let mut prev = ' '; // The initial value doesn't really matter
+    new_str.retain(|ch| {
+        let result = ch != ' ' || prev != ' ';
+        prev = ch;
+        result
+    });
+    new_str
+}
+
+#[derive(Debug, Default)]
+struct OldPseudo {
+    items: HashMap<String, Expr>,
+}
+
+impl VisitMut for OldPseudo {
+    fn visit_expr_mut(&mut self, item: &mut Expr) {
+        if let Expr::Call(ref node) = item {
+            if let syn::Expr::Path(call) = node.func.as_ref() {
+                let segments = &call.path.segments;
+                if let Some(path) = segments.first() {
+                    if path.ident.to_string() == "old" {
+                        // println!("Call: {item:?}");
+                        let name = format!("{}", quote!(#node));
+                        let cleaned: String = name
+                            .chars()
+                            .filter_map(|x| match x {
+                                'A'..='Z' | 'a'..='z' | '0'..='9' | ' ' => Some(x),
+                                _ => None,
+                            })
+                            .collect();
+
+                        let name = trim_whitespace(&cleaned)
+                            .split(' ')
+                            .filter(|s| !s.is_empty())
+                            .collect::<Vec<_>>()
+                            .join("_");
+
+                        let var = format_ident!("{name}");
+
+                        let old_arg = node
+                            .args
+                            .first()
+                            .expect("The 'old' pseudo-function have exactly one parameter");
+
+                        // store node as `old` pseudo-expressions
+                        self.items.insert(name, old_arg.clone());
+
+                        let expr: ExprPath = syn::parse_quote!(#var);
+                        *item = Expr::Path(expr);
+                    }
+                }
+            }
+        }
+        // Delegate to the default impl to visit nested expressions.
+        syn::visit_mut::visit_expr_mut(self, item);
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct Segment {
+    pub expressions: Vec<Expr>,
+}
+
+impl Parse for Segment {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let vars = Punctuated::<Expr, Token![,]>::parse_terminated(input).unwrap();
+        let expressions: Vec<Expr> = vars.into_iter().collect();
+
+        // println!("{items:#?}");
+        Ok(Segment { expressions })
+    }
+}
+
+#[derive(Default, Debug)]
+pub struct ContractAspectState {
+    pub docs: VecDeque<String>,
+    pub requires: Vec<Contract>,
+    pub invariants: Vec<Contract>,
+    pub ensures: Vec<Contract>,
+    pub aspects: Vec<Aspect>,
+}
+
+impl ContractAspectState {
+    // Process rest of contract attributes
+    // and keep others, to generate new attributes set
+    pub fn process(&mut self, attrs: &Vec<Attribute>) -> Vec<Attribute> {
+        attrs
+            .iter()
+            .filter_map(|attr| {
+                // let len = attr.path.segments.len();
+                if let Some(PathSegment { ident, .. }) = attr.path.segments.last() {
+                    let pair = Type::type_and_mode(&ident.to_string());
+                    if let Some((ty, mode)) = pair {
+                        // create case
+                        match ty {
+                            Type::Requires | Type::Ensures | Type::Invariant => {
+                                let mut case = Contract::new(ty, mode);
+                                let tokens = attr.tokens.clone().into_iter().collect::<Vec<_>>();
+                                assert!(
+                                    tokens.len() == 1,
+                                    "Wrong contract sintax: not parenthesized expression"
+                                );
+
+                                if let Some(TokenTree::Group(group)) = tokens.first() {
+                                    case.parse_attributes(group.stream());
+                                    match ty {
+                                        Type::Requires => self.requires.push(case),
+                                        Type::Ensures => self.ensures.push(case),
+                                        Type::Invariant => self.invariants.push(case),
+                                        _ => unreachable!(),
+                                    }
+                                } else {
+                                    panic!("Wrong contract sintax: not parenthesized expression")
+                                }
+                            }
+                            Type::Aspect => {
+                                let tokens = attr.tokens.clone().into_iter().collect::<Vec<_>>();
+                                assert!(
+                                    tokens.len() == 1,
+                                    "Wrong aspect sintax: not parenthesized expression"
+                                );
+                                if let Some(TokenTree::Group(group)) = tokens.first() {
+                                    let aspect = Aspect::new(group.stream());
+                                    self.aspects.push(aspect);
+                                } else {
+                                    panic!("Wrong contract sintax: not parenthesized expression")
+                                }
+                            }
+                        };
+
+                        // Eat contract attribute
+                        None
+                    } else {
+                        // Attribute is not contract
+                        // Collect docs attributes
+                        if attr.path.is_ident("doc") {
+                            match attr.parse_meta().unwrap() {
+                                Meta::NameValue(MetaNameValue {
+                                    lit: Lit::Str(lit_str),
+                                    ..
+                                }) => {
+                                    // println!("{}", lit_str.value());
+                                    self.docs.push_back(lit_str.value());
+                                    None
+                                }
+                                _ => Some(attr.clone()),
+                            }
+                        } else {
+                            Some(attr.clone())
+                        }
+                    }
+                } else {
+                    // Attribute without path
+                    unreachable!()
+                }
+            })
+            .collect::<Vec<_>>()
+    }
+
+    // collect variable declarations from ensure cases
+    pub fn variables(&self) -> Vec<TokenStream2> {
+        let mut decls: HashMap<String, Expr> = HashMap::new();
+        self.ensures.iter().for_each(|case| {
+            case.decls.iter().for_each(|(name, expr)| {
+                decls.insert(name.to_string(), expr.clone());
+            });
+        });
+
+        decls
+            .into_iter()
+            .map(|(name, expr)| {
+                let name = format_ident!("{name}");
+                quote! {
+                    let #name = #expr;
+                }
+            })
+            .collect::<Vec<_>>()
+    }
+}
+
+pub trait SyntaxAndDocs {
+    fn generate(&self) -> (Vec<TokenStream2>, Vec<String>);
+}
+
+impl SyntaxAndDocs for Vec<Contract> {
+    fn generate(&self) -> (Vec<TokenStream2>, Vec<String>) {
+        let mut docs = vec![];
+        let stream = self
+            .iter()
+            .map(|case| {
+                if let Some(ref desc) = case.desc {
+                    docs.push(format!(" - {}", desc))
+                }
+                case.syntax()
+            })
+            .flatten()
+            .collect::<Vec<_>>();
+        (stream, docs)
+    }
+}
+
+// process macro attribute by initiating currentt case and handle rest of cases
+pub(crate) fn contracts_aspect(ty: Type, mode: Mode, attrs: TokenStream2, item: TokenStream2) -> TokenStream2 {
+    let mut state: ContractAspectState = ContractAspectState::default();
+
+    // process primary contract case
+
+    match ty {
+        Type::Requires | Type::Ensures | Type::Invariant => {
+            let mut case = Contract::new(ty, mode);
+            case.parse_attributes(attrs);
+
+            match ty {
+                Type::Requires => state.requires.push(case),
+                Type::Ensures => state.ensures.push(case),
+                Type::Invariant => state.invariants.push(case),
+                _ => unreachable!(),
+            }
+        }
+        Type::Aspect => {
+            state.aspects.push(Aspect::new(attrs));
+        }
+    }
+
+    // process rest of contract cases
+    let input: syn::ItemFn = syn::parse2(item.clone().into()).unwrap();
+
+    let syn::ItemFn {
+        attrs,
+        vis,
+        sig,
+        block,
+    } = input;
+
+    let attrs = state.process(&attrs);
+
+    let variables = state.variables();
+
+    // генерация синтаксиса и документации
+    let mut contract_docs: VecDeque<String> = VecDeque::new();
+
+    let (requires, docs) = state.requires.generate();
+    contract_docs.extend(docs);
+
+    let (invariants, docs) = state.invariants.generate();
+    contract_docs.extend(docs);
+
+    let (ensures, docs) = state.ensures.generate();
+    contract_docs.extend(docs);
+
+    if !contract_docs.is_empty() {
+        contract_docs.push_front(String::from(" # Contract"));
+    }
+
+    let before = state
+        .aspects
+        .iter()
+        .filter_map(|aspect| {
+            if let Some(item) = &aspect.before && let Some(block) = &item.default {
+            let stmts = &block.stmts;
+            Some(quote!{
+                #(#stmts)*
+            })
+        } else {
+            None
+        }
+        })
+        .collect::<Vec<_>>();
+
+    let after = state
+        .aspects
+        .iter()
+        .rev()
+        .filter_map(|aspect| {
+            if let Some(item) = &aspect.after && let Some(block) = &item.default {
+                let stmts = &block.stmts;
+                Some(quote!{
+                    #(#stmts)*
+                })
+            } else {
+                None
+            }
+        })
+        .collect::<Vec<_>>();
+
+    // Here we should deal with call name )))
+    // For traits we need only change some in output
+    let mut around = quote! {inner()};
+    let mut has_around = false;
+
+    let mut it = state.aspects.iter().rev().peekable();
+    while let Some(aspect) = it.next() {
+        if let Some(item) = &aspect.around && let Some(block) = &item.default {
+
+            let mut replacer = AspectJointPoint {
+                stream: &around,
+            };
+            
+            let mut stmts = block.stmts.clone();
+
+            for stmt in stmts.iter_mut() {
+                replacer.visit_stmt_mut(stmt);
+            }
+            
+            if it.peek().is_none() {
+                around = quote!(#(#stmts)*);
+            } else {
+                around = quote!({#(#stmts)*});
+            }
+            has_around = true;
+        }
+    }
+
+    let attrs = {
+        let mut new_attrs: Vec<Attribute> = Vec::new();
+
+        if !state.docs.is_empty() {
+            let mut it = state.docs.iter().peekable();
+            while let Some(comment) = it.next() {
+                if it.peek().is_some() {
+                    new_attrs.push(syn::parse_quote!(#[doc = #comment]));
+                } else if !comment.trim().is_empty() {
+                    new_attrs.push(syn::parse_quote!(#[doc = #comment]));
+                }
+            }
+            new_attrs.push(syn::parse_quote!(#[doc = ""]));
+        }
+
+        if !contract_docs.is_empty() {
+            let mut it = contract_docs.iter().peekable();
+            while let Some(comment) = it.next() {
+                if it.peek().is_some() {
+                    new_attrs.push(syn::parse_quote!(#[doc = #comment]));
+                } else if !comment.trim().is_empty() {
+                    new_attrs.push(syn::parse_quote!(#[doc = #comment]));
+                }
+            }
+            new_attrs.push(syn::parse_quote!(#[doc = ""]));
+        }
+
+        let mut aspect_docs: VecDeque<String> = VecDeque::new();
+        // TODO: Aspects
+        state.aspects.iter().for_each(|aspect| {
+            //
+            let mut docs = aspect.documentation();
+            if !docs.is_empty() {
+                docs.push_back(String::new());
+                aspect_docs.extend(docs);
+            }
+        });
+
+        if !aspect_docs.is_empty() {
+            aspect_docs.push_front(String::from(" # Aspects"));
+            let mut it = aspect_docs.iter().peekable();
+            while let Some(comment) = it.next() {
+                if it.peek().is_some() {
+                    new_attrs.push(syn::parse_quote!(#[doc = #comment]));
+                } else if !comment.trim().is_empty() {
+                    new_attrs.push(syn::parse_quote!(#[doc = #comment]));
+                }
+            }
+            new_attrs.push(syn::parse_quote!(#[doc = ""]));
+        }
+        attrs.iter().cloned().for_each(|attr| {
+            new_attrs.push(attr);
+        });
+        new_attrs
+    };
+
+    let stmts = &block.stmts;
+    let result = if has_around {
+        quote! {
+            let result = {
+                #around
+            };
+        }
+    } else {
+        quote! {
+            let result = inner();
+        }
+    };
+
+    quote! {
+        #(#attrs)* #vis #sig {
+
+            #(#requires)*
+            #(#invariants)*
+
+            #(#variables)*
+
+            let inner = || {
+                #(#stmts)*
+            };
+
+            #(#before)*
+
+            #result
+
+            #(#after)*
+
+            #(#invariants)*
+            #(#ensures)*
+
+            result
+        }
+    }
+}
\ No newline at end of file
diff --git a/macro/src/impls/decorate.rs b/macro/src/impls/decorate.rs
new file mode 100644
index 0000000..f41359d
--- /dev/null
+++ b/macro/src/impls/decorate.rs
@@ -0,0 +1,109 @@
+use std::{error, fmt, result};
+
+use proc_macro::TokenStream;
+use proc_macro2::TokenTree;
+use syn::*;
+
+type Result<T> = result::Result<T, Box<dyn error::Error>>;
+
+#[derive(Debug, PartialEq)]
+enum DecoratorError {
+    InvaludTokenStream,
+    DecoratorNotFound,
+}
+
+impl std::fmt::Display for DecoratorError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            DecoratorError::InvaludTokenStream => write!(f, "Invalid token stream"),
+            DecoratorError::DecoratorNotFound => write!(f, "Decorator name not found"),
+        }
+    }
+}
+
+impl error::Error for DecoratorError {}
+
+#[derive(Debug, PartialEq)]
+enum DecoratorAttr {
+    Fixed { name: Ident },
+    Parametric { name: Ident, args: Vec<Expr> },
+}
+
+impl DecoratorAttr {
+    fn parse(attr: proc_macro2::TokenStream) -> Result<Self> {
+        let mut ident = None;
+        let mut args = Vec::new();
+        for at in attr {
+            match at {
+                TokenTree::Ident(id) => {
+                    ident = Some(id);
+                }
+                TokenTree::Group(grp) => {
+                    if ident.is_none() {
+                        return Err(DecoratorError::InvaludTokenStream)?;
+                    }
+                    for t in grp.stream() {
+                        if let Ok(expr) = syn::parse2(t.into()) {
+                            args.push(expr);
+                        }
+                    }
+                }
+                _ => return Err(DecoratorError::InvaludTokenStream)?,
+            }
+        }
+        if let Some(name) = ident {
+            if args.is_empty() {
+                Ok(DecoratorAttr::Fixed { name })
+            } else {
+                Ok(DecoratorAttr::Parametric { name, args })
+            }
+        } else {
+            return Err(DecoratorError::DecoratorNotFound)?;
+        }
+    }
+}
+
+pub(crate) fn decorate(attr: TokenStream, func: TokenStream) -> TokenStream {
+    let func = func.into();
+    let item_fn: ItemFn = syn::parse(func).expect("Input is not a function");
+    let vis = &item_fn.vis;
+    let ident = &item_fn.sig.ident;
+    let block = &item_fn.block;
+
+    let inputs = item_fn.sig.inputs;
+    let output = item_fn.sig.output;
+
+    let input_values: Vec<_> = inputs
+        .iter()
+        .map(|arg| match arg {
+            &FnArg::Typed(ref val) => &val.pat,
+            _ => unimplemented!("#[decorate] cannot be used with associated function"),
+        })
+        .collect();
+
+    let attr = DecoratorAttr::parse(attr.into()).expect("Failed to parse attribute");
+    let caller = match attr {
+        DecoratorAttr::Fixed { name } => {
+            quote::quote! {
+                #vis fn #ident(#inputs) #output {
+                    let f = #name(deco_internal);
+                    return f(#(#input_values,) *);
+
+                    fn deco_internal(#inputs) #output #block
+                }
+            }
+        }
+        DecoratorAttr::Parametric { name, args } => {
+            quote::quote! {
+                #vis fn #ident(#inputs) #output {
+                    let deco = #name(#(#args,) *);
+                    let f = deco(deco_internal);
+                    return f(#(#input_values,) *);
+
+                    fn deco_internal(#inputs) #output #block
+                }
+            }
+        }
+    };
+    caller.into()
+}
\ No newline at end of file
diff --git a/macro/src/impls/delegate.rs b/macro/src/impls/delegate.rs
new file mode 100644
index 0000000..fa13a4c
--- /dev/null
+++ b/macro/src/impls/delegate.rs
@@ -0,0 +1,213 @@
+use proc_macro2::{Group, Span, TokenStream, TokenTree};
+use quote::{quote, quote_spanned, ToTokens};
+use syn::spanned::Spanned;
+use syn::{
+    parse_macro_input, Expr, ExprParen, FnArg, ImplItem, ImplItemMethod, ItemImpl, Pat, ReturnType,
+};
+
+fn delegate_input(input: TokenStream, receiver: &Expr) -> TokenStream {
+    if let Ok(input) = syn::parse2::<ItemImpl>(input.clone()) {
+        return delegate_impl_block(input, receiver);
+    }
+    if let Ok(input) = syn::parse2::<ImplItemMethod>(input.clone()) {
+        return delegate_method(input, receiver);
+    }
+    let mut tokens = input.into_iter();
+    let first_non_attr_token = 'outer: loop {
+        match tokens.next() {
+            None => break None,
+            Some(TokenTree::Punct(p)) if p.as_char() == '#' => {}
+            Some(token) => break Some(token),
+        }
+        loop {
+            match tokens.next() {
+                None => break 'outer None,
+                Some(TokenTree::Punct(_)) => {}
+                Some(TokenTree::Group(_)) => continue 'outer,
+                Some(token) => break 'outer Some(token),
+            }
+        }
+    };
+    if let Some(token) = first_non_attr_token {
+        let msg = match &token {
+            TokenTree::Ident(ident) if ident == "impl" => "invalid impl block for #[delegate]",
+            TokenTree::Ident(ident) if ident == "fn" => "invalid method for #[delegate]",
+            _ => "expected an impl block or method inside impl block",
+        };
+        quote_spanned! { token.span() => compile_error!(#msg); }
+    } else {
+        panic!("unexpected eof")
+    }
+}
+
+fn delegate_impl_block(input: ItemImpl, receiver: &Expr) -> TokenStream {
+    let ItemImpl {
+        attrs,
+        defaultness,
+        unsafety,
+        impl_token,
+        mut generics,
+        trait_,
+        self_ty,
+        brace_token: _,
+        items,
+    } = input;
+    let where_clause = generics.where_clause.take();
+    let trait_ = trait_.map(|(bang, path, for_)| quote!(#bang #path #for_));
+    let items = items.into_iter().map(|item| {
+        let method = match item {
+            ImplItem::Method(m) => m,
+            _ => return item.into_token_stream(),
+        };
+        delegate_method(method, receiver)
+    });
+
+    quote! {
+        #(#attrs)* #defaultness #unsafety #impl_token #generics #trait_ #self_ty #where_clause {
+            #(#items)*
+        }
+    }
+}
+
+fn delegate_method(input: ImplItemMethod, receiver: &Expr) -> TokenStream {
+    let ImplItemMethod {
+        mut attrs,
+        vis,
+        defaultness,
+        sig,
+        block: _,
+    } = input;
+    let mut errors = TokenStream::new();
+    let mut push_error = |span: Span, msg: &'static str| {
+        errors.extend(quote_spanned! { span => compile_error!(#msg); });
+    };
+    // Parse attributes.
+    let mut has_inline = false;
+    let mut has_into = false;
+    let mut call_name = None;
+    attrs.retain(|attr| {
+        let path = &attr.path;
+        if path.is_ident("inline") {
+            has_inline = true;
+        } else if path.is_ident("into") {
+            if !attr.tokens.is_empty() {
+                push_error(attr.tokens.span(), "unexpected argument");
+            }
+            if has_into {
+                push_error(attr.span(), "duplicate #[into] attribute");
+            }
+            has_into = true;
+            return false;
+        } else if path.is_ident("call") {
+            match syn::parse2::<ExprParen>(attr.tokens.clone()) {
+                Ok(expr) if expr.attrs.is_empty() => {
+                    let inner = expr.expr;
+                    match &*inner {
+                        Expr::Path(path) if path.attrs.is_empty() && path.qself.is_none() => {
+                            if let Some(ident) = path.path.get_ident() {
+                                if call_name.is_some() {
+                                    push_error(attr.span(), "duplicate #[call] attribute");
+                                }
+                                call_name = Some(ident.clone());
+                            } else {
+                                push_error(
+                                    inner.span(),
+                                    "invalid argument, expected an identifier",
+                                );
+                            }
+                        }
+                        _ => push_error(inner.span(), "invalid argument, expected an identifier"),
+                    }
+                }
+                _ => push_error(attr.tokens.span(), "invalid argument"),
+            }
+            return false;
+        }
+        true
+    });
+    // Mark method always inline if it's not otherwise specified.
+    let inline = if !has_inline {
+        quote!(#[inline(always)])
+    } else {
+        quote!()
+    };
+    let mut inputs = sig.inputs.iter();
+    // Extract the self token.
+    let self_token = match inputs.next() {
+        Some(FnArg::Receiver(receiver)) => receiver.self_token.to_token_stream(),
+        Some(FnArg::Typed(pat)) => match &*pat.pat {
+            Pat::Ident(ident) if ident.ident == "self" => ident.ident.to_token_stream(),
+            _ => {
+                push_error(pat.span(), "expected self");
+                TokenStream::new()
+            }
+        },
+        None => {
+            push_error(sig.paren_token.span, "expected self");
+            TokenStream::new()
+        }
+    };
+    // List all parameters.
+    let args = inputs
+        .filter_map(|arg| match arg {
+            FnArg::Typed(pat) => match &*pat.pat {
+                Pat::Ident(ident) => Some(ident.to_token_stream()),
+                _ => {
+                    push_error(pat.pat.span(), "expect an identifier");
+                    None
+                }
+            },
+            _ => {
+                push_error(arg.span(), "unexpected argument");
+                None
+            }
+        })
+        .collect::<Vec<_>>();
+    // Return errors if any.
+    if !errors.is_empty() {
+        return errors;
+    } else {
+        // Drop it to ensure that we are not pushing anymore into it.
+        drop(errors);
+    }
+    // Generate method call.
+    let name = call_name.as_ref().unwrap_or(&sig.ident);
+    // Replace the self token in the receiver with the token we extract above to ensure it comes
+    // from the right hygiene context.
+    let receiver = replace_self(receiver.to_token_stream(), &self_token);
+    let body = quote! { #receiver.#name(#(#args),*) };
+    let body = match &sig.output {
+        ReturnType::Default => quote! { #body; },
+        ReturnType::Type(_, ty) if has_into => {
+            quote! { ::std::convert::Into::<#ty>::into(#body) }
+        }
+        _ => body,
+    };
+    quote! {
+        #(#attrs)* #inline #vis #defaultness #sig {
+            #body
+        }
+    }
+}
+
+fn replace_self(expr: TokenStream, self_token: &TokenStream) -> TokenStream {
+    expr.into_iter()
+        .map(|token| match token {
+            TokenTree::Ident(ident) if ident == "self" => self_token.clone(),
+            TokenTree::Group(group) => {
+                let delimiter = group.delimiter();
+                let stream = replace_self(group.stream(), self_token);
+                Group::new(delimiter, stream).into_token_stream()
+            }
+            _ => token.into_token_stream(),
+        })
+        .collect()
+}
+
+pub(crate) fn delegate(
+    attr: proc_macro::TokenStream,
+    item: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+    let receiver = parse_macro_input!(attr as Expr);
+    delegate_input(item.into(), &receiver).into()
+}
\ No newline at end of file
diff --git a/macro/src/impls/mock.rs b/macro/src/impls/mock.rs
new file mode 100644
index 0000000..11f5eec
--- /dev/null
+++ b/macro/src/impls/mock.rs
@@ -0,0 +1,217 @@
+use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+
+use quote::{format_ident, quote};
+use syn::{FnArg, Item, ItemTrait, ReturnType, TraitItem, TypeParamBound};
+
+fn process_item_trait(mut input: ItemTrait) -> TokenStream2 {
+    let trait_name = input.ident.clone();
+    let mock_name = format_ident!("Mock{}", trait_name);
+    let builder_name = format_ident!("Mock{}Builder", trait_name);
+
+    // Fix default supertrait
+    {
+        let mut has_default = false;
+        input.supertraits.iter().for_each(|item| {
+            //
+            if let TypeParamBound::Trait(val) = item {
+                let name = val.path.segments.last().unwrap().ident.to_string();
+                if &name == "Default" {
+                    has_default = true;
+                }
+            }
+        });
+        if !has_default {
+            let value: syn::TraitBound = syn::parse_quote! {Default};
+            input.supertraits.insert(0, TypeParamBound::Trait(value));
+        }
+    }
+
+    // Collect method names and generate mock method
+    let fields = input
+        .items
+        .iter()
+        .filter_map(|item| {
+            if let TraitItem::Method(method) = item {
+                Some(format_ident!("__mock_{}__", method.sig.ident))
+            } else {
+                None
+            }
+        })
+        .collect::<Vec<_>>();
+    let mock_method: syn::TraitItemMethod = syn::parse_quote! {
+        fn mock() -> #builder_name<Self> {
+            #builder_name {
+                inner: #mock_name {
+                    __mock_fallback__: Default::default(),
+                    #(#fields: None,)*
+                }
+            }
+        }
+    };
+
+    let mut builder_methods: Vec<TokenStream2> = vec![];
+    // Generate mock struct
+    let mock = {
+        let fields = input
+            .items
+            .iter()
+            .filter_map(|item| {
+                if let TraitItem::Method(method) = item {
+                    let inputs = method
+                        .sig
+                        .inputs
+                        .iter()
+                        .filter_map(|input| {
+                            if let FnArg::Typed(typed) = input {
+                                Some(typed.ty.clone())
+                            } else {
+                                None
+                            }
+                        })
+                        .collect::<Vec<_>>();
+                    let field = format_ident!("__mock_{}__", method.sig.ident);
+                    match method.sig.output {
+                        ReturnType::Default => {
+                            let builder_method = {
+                                let method_name = &method.sig.ident;
+                                quote! {
+                                    fn #method_name<F>(mut self, when: F) -> Self
+                                    where
+                                        F: Fn(#(#inputs),*) + 'static,
+                                    {
+                                        self.inner.#field = Some(Box::new(when));
+                                        self
+                                    }
+                                }
+                            };
+                            builder_methods.push(builder_method);
+                            Some(quote! {#field: Option<Box<dyn Fn(#(#inputs),*)>>})
+                        }
+                        ReturnType::Type(_, ref ty) => {
+                            let output = ty.clone();
+                            let builder_method = {
+                                let method_name = &method.sig.ident;
+                                quote! {
+                                    fn #method_name<F>(mut self, when: F) -> Self
+                                    where
+                                        F: Fn(#(#inputs),*) -> #output + 'static,
+                                    {
+                                        self.inner.#field = Some(Box::new(when));
+                                        self
+                                    }
+                                }
+                            };
+                            builder_methods.push(builder_method);
+                            Some(quote! {#field: Option<Box<dyn Fn(#(#inputs),*) -> #output>>})
+                        }
+                    }
+                } else {
+                    None
+                }
+            })
+            .collect::<Vec<_>>();
+
+        quote! {
+            #[derive(Default)]
+            struct #mock_name<T>
+            where
+                T: #trait_name,
+            {
+                __mock_fallback__: T,
+                #(#fields),*
+            }
+        }
+    };
+
+    let mock_impl = {
+        let methods = input
+            .items
+            .iter()
+            .filter_map(|item| {
+                if let TraitItem::Method(method) = item {
+                    let sig = method.sig.clone();
+                    let args = sig
+                        .inputs
+                        .iter()
+                        .filter_map(|input| {
+                            if let FnArg::Typed(syn::PatType { pat, .. }) = input {
+                                if let syn::Pat::Ident(ident) = pat.as_ref() {
+                                    Some(&ident.ident)
+                                } else {
+                                    panic!("Unhandled function argument");
+                                }
+                            } else {
+                                None
+                            }
+                        })
+                        .collect::<Vec<_>>();
+                    let ident = &sig.ident;
+                    let field = format_ident!("__mock_{}__", sig.ident);
+                    Some(quote! {
+                        #sig {
+                            match self.#field {
+                                Some(ref func) => func(#(#args),*),
+                                None => self.__mock_fallback__.#ident(#(#args),*),
+                            }
+                        }
+                    })
+                } else {
+                    None
+                }
+            })
+            .collect::<Vec<_>>();
+
+        quote! {
+            impl<T> #trait_name for #mock_name<T>
+            where
+                T: #trait_name,
+            {
+                #(#methods)*
+            }
+        }
+    };
+
+    let builder = quote! {
+        struct #builder_name<T>
+        where
+            T: #trait_name,
+        {
+            inner: #mock_name<T>,
+        }
+    };
+
+    let builder_impl = quote! {
+        impl<T> #builder_name<T>
+        where
+            T: #trait_name,
+        {
+            #(#builder_methods)*
+
+            fn build(self) -> #mock_name<T> {
+                self.inner
+            }
+        }
+    };
+    // output
+    input.items.push(TraitItem::Method(mock_method));
+    quote! {
+        #input
+
+        #mock
+
+        #mock_impl
+
+        #builder
+
+        #builder_impl
+    }
+}
+
+pub(crate) fn mock(_attr: TokenStream, input: TokenStream) -> TokenStream {
+    let item: Item = syn::parse2(input.into()).unwrap();
+    match item {
+        Item::Trait(item) => process_item_trait(item).into(),
+        _ => panic!("Traits only supported for mocking"),
+    }
+}
\ No newline at end of file
diff --git a/macro/src/impls/mod.rs b/macro/src/impls/mod.rs
new file mode 100644
index 0000000..f83d930
--- /dev/null
+++ b/macro/src/impls/mod.rs
@@ -0,0 +1,225 @@
+#![allow(dead_code)]
+#[allow(unused_imports)]
+use proc_macro::TokenStream;
+use serde::Deserialize;
+
+mod compose;
+pub(crate) use self::compose::*;
+
+mod contract_aspect;
+pub(crate) use self::contract_aspect::*;
+
+mod decorate;
+pub(crate) use self::decorate::*;
+
+mod delegate;
+pub(crate) use self::delegate::*;
+
+mod mock;
+pub(crate) use self::mock::*;
+
+mod register;
+pub(crate) use self::register::*;
+
+mod utils;
+
+#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize)]
+struct MainConfig {
+    #[serde(default)]
+    backtrace: BacktraceConfig,
+    #[serde(default)]
+    log: LogConfig,
+}
+
+#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize)]
+struct LogConfig {
+    #[serde(default)]
+    level: LogLevel,
+    #[serde(default)]
+    output: LogOutput,
+}
+
+#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize)]
+enum LogOutput {
+    #[default]
+    StdOut,
+    Syslog,
+    File(String),
+}
+
+#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize)]
+enum LogLevel {
+    Trace,
+    Debug,
+    #[default]
+    Info,
+    Warn,
+    Error,
+}
+
+#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize)]
+struct BacktraceConfig {
+    #[serde(default)]
+    level: BactraceLevel,
+}
+
+#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize)]
+enum BactraceLevel {
+    None,
+    #[default]
+    Short,
+    Full,
+}
+
+// #[cfg(not(test))] // Work around for rust-lang/rust#62127
+pub(crate) fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
+    let attr = proc_macro2::TokenStream::from(attr);
+    let config = if !attr.is_empty() {
+        match serde_tokenstream::from_tokenstream::<MainConfig>(&attr) {
+            Ok(config) => config,
+            Err(err) => return err.to_compile_error().into(),
+        }
+    } else {
+        MainConfig::default()
+    };
+
+    let input: syn::ItemFn = syn::parse2(item.into()).unwrap();
+
+    let backtrace_level = match config.backtrace.level {
+        BactraceLevel::None => quote::quote!("0"),
+        BactraceLevel::Short => quote::quote!("1"),
+        BactraceLevel::Full => quote::quote!("full"),
+    };
+
+    let log_level = match config.log.level {
+        LogLevel::Trace => quote::quote!("trace"),
+        LogLevel::Debug => quote::quote!("debug"),
+        LogLevel::Info => quote::quote!("info"),
+        LogLevel::Warn => quote::quote!("warn"),
+        LogLevel::Error => quote::quote!("error"),
+    };
+
+    let log_initialization = match config.log.output {
+        LogOutput::StdOut => {
+            quote::quote!({
+                use logging::{
+                    colors::{Color, ColoredLevelConfig},
+                    Dispatch, LevelFilter,
+                };
+
+                let colors = ColoredLevelConfig::new()
+                    .debug(Color::Blue)
+                    .info(Color::Green)
+                    .warn(Color::Yellow)
+                    .error(Color::Red)
+                    .trace(Color::BrightBlack);
+
+                Dispatch::new()
+                    .format(move |out, message, record| {
+                        out.finish(format_args!(
+                            "[{} {:5} {}] {}",
+                            format_rfc3339(std::time::SystemTime::now()),
+                            colors.color(record.level()),
+                            record.target(),
+                            message
+                        ))
+                    })
+                    // .level(LevelFilter::Debug)
+                    .chain(std::io::stdout())
+                    .apply()
+                    .unwrap();
+            })
+        }
+        LogOutput::Syslog => quote::quote!({
+            use logging::{Dispatch, LevelFilter};
+
+            const CARGO_PKG_NAME: &'static str = env!("CARGO_PKG_NAME");
+
+            let formatter = logging::syslog::Formatter3164 {
+                facility: logging::syslog::Facility::LOG_USER,
+                hostname: None,
+                process: CARGO_PKG_NAME.to_owned(),
+                pid: 0,
+            };
+
+            Dispatch::new()
+                // Perform allocation-free log formatting
+                .format(move |out, message, record| {
+                    out.finish(format_args!(
+                        "[{} {:5} {}] {}",
+                        format_rfc3339(std::time::SystemTime::now()),
+                        record.level(),
+                        record.target(),
+                        message
+                    ))
+                })
+                .chain(logging::syslog::unix(formatter).unwrap())
+                .apply()
+                .unwrap();
+        }),
+        LogOutput::File(filename) => quote::quote!({
+            use logging::{
+                colors::{Color, ColoredLevelConfig},
+                Dispatch, LevelFilter,
+            };
+
+            Dispatch::new()
+                // Perform allocation-free log formatting
+                .format(move |out, message, record| {
+                    out.finish(format_args!(
+                        "[{} {:5} {}] {}",
+                        format_rfc3339(std::time::SystemTime::now()),
+                        record.level(),
+                        record.target(),
+                        message
+                    ))
+                })
+                .chain(logging::log_file(#filename).unwrap())
+                .apply()
+                .unwrap();
+        }),
+    };
+
+    let syn::ItemFn {
+        attrs,
+        vis,
+        sig,
+        block,
+    } = input;
+
+    let stmts = &block.stmts;
+    let output = quote::quote! {
+        #(#attrs)* #vis #sig {
+            if cfg!(debug_assertions) {
+                use std::env;
+
+                if env::var("RUST_BACKTRACE").is_err() {
+                    env::set_var("RUST_BACKTRACE", #backtrace_level);
+                }
+
+                if env::var("RUST_LOG").is_err() {
+                    env::set_var("RUST_LOG", #log_level);
+                }
+
+                panic::backtrace::install();
+            } else {
+                // Build the metadata from the build environment
+                const PACKAGE_METADATA: panic::PackageMetadata<'static> = panic::PackageMetadata {
+                    pkg_name: env!("CARGO_PKG_NAME"),
+                    crate_name: env!("CARGO_CRATE_NAME"),
+                    version: env!("CARGO_PKG_VERSION"),
+                    repository: env!("CARGO_PKG_REPOSITORY"),
+                };
+
+                // Append the new panic handler
+                panic::handler(panic::suggest_issue_tracker, PACKAGE_METADATA);
+            }
+
+            #log_initialization
+
+            #(#stmts)*
+        }
+    };
+
+    output.into()
+}
diff --git a/macro/src/impls/register.rs b/macro/src/impls/register.rs
new file mode 100644
index 0000000..278d32c
--- /dev/null
+++ b/macro/src/impls/register.rs
@@ -0,0 +1,459 @@
+use std::{
+    collections::VecDeque,
+    net::UdpSocket,
+};
+
+use companion::{companion_addr, Task, Response};
+use proc_macro2::{Span, TokenStream as TokenStream2};
+use quote::{quote, ToTokens};
+use syn::{
+    visit_mut::VisitMut,
+    Attribute, FnArg, Ident, Item,
+     ItemTrait, Pat, Path, 
+    TraitItem, TraitItemMethod,
+};
+
+use crate::MAX_UDP_PAYLOAD;
+
+use super::{AspectJointPoint, Type, ContractAspectState, SyntaxAndDocs};
+
+/// Name used for the "re-routed" method.
+fn trait_method_impl_name(name: &str) -> String {
+    format!("__impl_trait_{}", name)
+}
+
+fn method_rename(method: &TraitItemMethod) -> TraitItemMethod {
+    let mut method: TraitItemMethod = (*method).clone();
+    let name = trait_method_impl_name(&method.sig.ident.to_string());
+
+    let mut attrs = vec![];
+    attrs.push(syn::parse_quote!(#[doc(hidden)]));
+    attrs.push(syn::parse_quote!(#[doc = " This is an internal function that is not meant to be used directly!"]));
+    attrs
+        .push(syn::parse_quote!(#[doc = " See the documentation of the `#[register]` attribute."]));
+
+    // add all existing non-contract attributes
+    attrs.extend(
+        method
+            .attrs
+            .iter()
+            .filter(|attr| {
+                let name = attr.path.segments.last().unwrap().ident.to_string();
+
+                Type::type_and_mode(&name).is_none()
+                    && name != "aspect"
+                    && name != "doc"
+            })
+            .cloned(),
+    );
+
+    method.attrs = attrs;
+    method.sig.ident = Ident::new(&name, method.sig.ident.span());
+
+    method
+}
+
+struct ArgInfo {
+    call_toks: proc_macro2::TokenStream,
+}
+
+// Calculate name and pattern tokens
+fn arg_pat_info(pat: &Pat) -> ArgInfo {
+    match pat {
+        Pat::Ident(ident) => {
+            let toks = quote::quote! {
+                #ident
+            };
+            ArgInfo { call_toks: toks }
+        }
+        Pat::Tuple(tup) => {
+            let infos = tup.elems.iter().map(arg_pat_info);
+
+            let toks = {
+                let mut toks = proc_macro2::TokenStream::new();
+
+                for info in infos {
+                    toks.extend(info.call_toks);
+                    toks.extend(quote::quote!(,));
+                }
+
+                toks
+            };
+
+            ArgInfo {
+                call_toks: quote::quote!((#toks)),
+            }
+        }
+        Pat::TupleStruct(_tup) => unimplemented!(),
+        p => panic!("Unsupported pattern type: {:?}", p),
+    }
+}
+
+#[allow(unused_variables)]
+fn process_item_trait(path: String, mut input: ItemTrait) -> TokenStream2 {
+    // create method wrappers and renamed items
+    let funcs = input
+        .items
+        .iter()
+        .filter_map(|item| {
+            if let TraitItem::Method(method) = item {
+                let rename = method_rename(method);
+                let wrapper = {
+                    // create method wrapper
+                    let mut method = (*method).clone();
+                    let args = method
+                        .sig
+                        .inputs
+                        .clone()
+                        .into_iter()
+                        .map(|arg| {
+                            //
+                            match &arg {
+                                FnArg::Receiver(_) => quote! {self},
+                                FnArg::Typed(p) => {
+                                    let info = arg_pat_info(&p.pat);
+
+                                    info.call_toks
+                                }
+                            }
+                        })
+                        .collect::<Vec<_>>();
+                    let arguments = {
+                        let mut toks = proc_macro2::TokenStream::new();
+
+                        for arg in args {
+                            toks.extend(arg);
+                            toks.extend(quote::quote!(,));
+                        }
+
+                        toks
+                    };
+
+                    // pre-process here
+                    let body: TokenStream2 = {
+                        let name = trait_method_impl_name(&method.sig.ident.to_string());
+                        let name = syn::Ident::new(&name, method.sig.ident.span());
+
+                        quote::quote! {
+                            {
+                                Self::#name(#arguments)
+                            }
+                        }
+                    };
+
+                    let mut attrs = vec![];
+
+                    // keep the documentation and contracts of the original method
+                    attrs.extend(
+                        method
+                            .attrs
+                            .iter()
+                            .filter(|attr| {
+                                let name = attr.path.segments.last().unwrap().ident.to_string();
+                                Type::type_and_mode(&name).is_some()
+                                    || name == "aspect"
+                                    || name == "doc"
+                            })
+                            .cloned(),
+                    );
+                    // always inline
+                    attrs.push(syn::parse_quote!(#[inline(always)]));
+
+                    // INFO: NEW PROCESS
+                    let mut state: ContractAspectState = ContractAspectState::default();
+                    let attrs = state.process(&attrs);
+
+                    let variables = state.variables();
+
+                    let mut contract_docs: VecDeque<String> = VecDeque::new();
+
+                    let (requires, docs) = state.requires.generate();
+                    contract_docs.extend(docs);
+
+                    let (invariants, docs) = state.invariants.generate();
+                    contract_docs.extend(docs);
+
+                    let (ensures, docs) = state.ensures.generate();
+                    contract_docs.extend(docs);
+
+                    if !contract_docs.is_empty() {
+                        contract_docs.push_front(String::from(" # Contract"));
+                    }
+
+                    let before = state
+                        .aspects
+                        .iter()
+                        .filter_map(|aspect| {
+                            if let Some(item) = &aspect.before && let Some(block) = &item.default {
+                                let stmts = &block.stmts;
+                                Some(quote!{
+                                    #(#stmts)*
+                                })
+                            } else {
+                                None
+                            }
+                        })
+                        .collect::<Vec<_>>();
+
+                    let after = state
+                        .aspects
+                        .iter()
+                        .rev()
+                        .filter_map(|aspect| {
+                            if let Some(item) = &aspect.after && let Some(block) = &item.default {
+                                    let stmts = &block.stmts;
+                                    Some(quote!{
+                                        #(#stmts)*
+                                    })
+                                } else {
+                                    None
+                                }
+                        })
+                        .collect::<Vec<_>>();
+
+                    // Here we should deal with call name )))
+                    // For traits we need only change some in output
+                    // let mut around = quote! {inner()}; // body
+                    let mut around = body.clone(); // body
+                    let callee = body.clone();
+                    let mut has_around = false;
+
+                    let mut it = state.aspects.iter().rev().peekable();
+                    while let Some(aspect) = it.next() {
+                        if let Some(item) = &aspect.around && let Some(block) = &item.default {
+                            
+                            let mut replacer = AspectJointPoint {
+                                stream: &around,
+                            };
+
+                            let mut stmts = block.stmts.clone();
+                            
+                            for stmt in stmts.iter_mut() {
+                                replacer.visit_stmt_mut(stmt);
+                            }
+                            
+                            if it.peek().is_none() {
+                                around = quote!(#(#stmts)*);
+                            } else {
+                                around = quote!({#(#stmts)*});
+                            }
+                            has_around = true;
+                        }
+                    }
+
+                    let attrs = {
+                        let mut new_attrs: Vec<Attribute> = Vec::new();
+
+                        if !state.docs.is_empty() {
+                            let mut it = state.docs.iter().peekable();
+                            while let Some(comment) = it.next() {
+                                if it.peek().is_some() {
+                                    new_attrs.push(syn::parse_quote!(#[doc = #comment]));
+                                } else if !comment.trim().is_empty() {
+                                    new_attrs.push(syn::parse_quote!(#[doc = #comment]));
+                                }
+                            }
+                            new_attrs.push(syn::parse_quote!(#[doc = ""]));
+                        }
+
+                        if !contract_docs.is_empty() {
+                            let mut it = contract_docs.iter().peekable();
+                            while let Some(comment) = it.next() {
+                                if it.peek().is_some() {
+                                    new_attrs.push(syn::parse_quote!(#[doc = #comment]));
+                                } else if !comment.trim().is_empty() {
+                                    new_attrs.push(syn::parse_quote!(#[doc = #comment]));
+                                }
+                            }
+                            new_attrs.push(syn::parse_quote!(#[doc = ""]));
+                        }
+
+                        let mut aspect_docs: VecDeque<String> = VecDeque::new();
+                        // INFO: Aspects
+                        state.aspects.iter().for_each(|aspect| {
+                            //
+                            let mut docs = aspect.documentation();
+                            if !docs.is_empty() {
+                                docs.push_back(String::new());
+                                aspect_docs.extend(docs);
+                            }
+                        });
+
+                        if !aspect_docs.is_empty() {
+                            aspect_docs.push_front(String::from(" # Aspects"));
+                            let mut it = aspect_docs.iter().peekable();
+                            while let Some(comment) = it.next() {
+                                if it.peek().is_some() {
+                                    new_attrs.push(syn::parse_quote!(#[doc = #comment]));
+                                } else if !comment.trim().is_empty() {
+                                    new_attrs.push(syn::parse_quote!(#[doc = #comment]));
+                                }
+                            }
+                            new_attrs.push(syn::parse_quote!(#[doc = ""]));
+                        }
+                        attrs.iter().cloned().for_each(|attr| {
+                            new_attrs.push(attr);
+                        });
+                        new_attrs
+                    };
+
+                    // INFO: END OF NEW PROCESS
+                    method.attrs = attrs;
+
+                    {
+                        let result = if has_around {
+                            quote! {
+                                let result = #callee;
+                            }
+                        } else {
+                            quote! {
+                                let result = {
+                                    #around
+                                };
+                            }
+                        };
+                        let body = quote! {
+                            {
+                                #(#requires)*
+                                #(#invariants)*
+
+                                #(#variables)*
+
+                                #(#before)*
+
+                                #result
+
+                                #(#after)*
+
+                                #(#invariants)*
+                                #(#ensures)*
+
+                                result
+                            }
+                        };
+                        let block: syn::Block = syn::parse2(body).unwrap();
+                        method.default = Some(block);
+                        method.semi_token = None;
+                    }
+
+                    method
+                };
+
+                Some(vec![TraitItem::Method(rename), TraitItem::Method(wrapper)])
+            } else {
+                None
+            }
+        })
+        .flatten()
+        .collect::<Vec<_>>();
+
+    // remove all previous methods
+    input.items = input
+        .items
+        .into_iter()
+        .filter(|item| {
+            //
+            match item {
+                TraitItem::Method(_) => false,
+                _ => true,
+            }
+        })
+        .collect();
+
+    // add back new methods
+    input.items.extend(funcs);
+
+    let _ = process_remote_trait(path, input.clone());
+
+    input.into_token_stream()
+}
+
+// Remote processing
+// Cutoff all attributes
+fn process_remote_trait(path: String, mut input: ItemTrait) -> TokenStream2 {
+    // Cutoff remote attributes
+    input.attrs = vec![];
+    input.items.iter_mut().for_each(|item| {
+        // Cutoff every remote methods attributes and body
+        if let TraitItem::Method(method) = item {
+            method.attrs = vec![];
+
+            if method.default.is_some() {
+                method.default = None;
+                method.semi_token = Some(syn::token::Semi(Span::call_site()));
+            }
+        }
+    });
+    let data = syn_serde::json::to_string(&input);
+
+    let mut buf = [0; MAX_UDP_PAYLOAD];
+
+    let addr = companion_addr();
+
+    let socket = UdpSocket::bind("[::]:0").unwrap();
+    socket.connect(addr).unwrap();
+
+    socket.send(&Task::Set(&path, &data).as_bytes()).unwrap();
+    let (len, _src) = socket.recv_from(&mut buf).unwrap();
+    let _resp = Response::from(&buf[..len]);
+
+    TokenStream2::new()
+}
+
+fn process_aspect_trait(path: String, input: ItemTrait) -> TokenStream2 {
+    input.items.iter().for_each(|item| {
+        // check input
+        match item {
+            TraitItem::Method(method) => {
+                let ident = method.sig.ident.to_string();
+                match ident.as_str() {
+                    "before" | "after" | "around" => {}
+                    _ => {
+                        panic!("Aspect supports only `before`, `after` and `around` methods")
+                    }
+                }
+            }
+            _ => panic!("Aspect definition support only methods"),
+        }
+    });
+    let data = syn_serde::json::to_string(&input);
+
+    let mut buf = [0; MAX_UDP_PAYLOAD];
+
+    let addr = companion_addr();
+
+    let socket = UdpSocket::bind("[::]:0").unwrap();
+    socket.connect(addr).unwrap();
+
+    socket.send(&Task::Set(&path, &data).as_bytes()).unwrap();
+    let (len, _src) = socket.recv_from(&mut buf).unwrap();
+    let _resp = Response::from(&buf[..len]);
+
+    input.into_token_stream()
+}
+
+pub(crate) fn register(attrs: TokenStream2, input: TokenStream2) -> TokenStream2 {
+    let path: Path = syn::parse2(attrs).unwrap();
+    let path = path
+        .segments
+        .iter()
+        .map(|item| item.ident.to_string())
+        .collect::<Vec<String>>()
+        .join("::");
+
+    let item: Item = syn::parse2(input).unwrap();
+    match item {
+        Item::Trait(item) => {
+            //
+            let ident = item.ident.to_string();
+            if ident.ends_with("Aspect") {
+                process_aspect_trait(path, item)
+            } else if ident.ends_with("Remote") {
+                process_remote_trait(path, item)
+            } else {
+                process_item_trait(path, item)
+            }
+        }
+        _ => panic!("Traits only supported for registration"),
+    }
+}
\ No newline at end of file
diff --git a/macro/src/impls/utils.rs b/macro/src/impls/utils.rs
new file mode 100644
index 0000000..f40f3c1
--- /dev/null
+++ b/macro/src/impls/utils.rs
@@ -0,0 +1,49 @@
+#![allow(dead_code, unused_variables)]
+use syn::{
+    parse::{Parse, ParseStream, Result},
+    punctuated::Punctuated,
+    Ident, Token, TypePath,
+};
+
+pub(crate) const MAX_UDP_PAYLOAD: usize = 65507;
+
+pub struct AdviceField {
+    member: Ident,
+    // colon: Token![:],
+    // value: syn::LitStr,
+    value: TypePath,
+}
+
+impl Parse for AdviceField {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let member: Ident = input.parse()?;
+        let colon_token: Token![:] = input.parse()?;
+        // let value: Expr = input.parse()?;
+        // let value: syn::LitStr = input.parse()?;
+        let value: TypePath = input.parse()?;
+
+        Ok(AdviceField {
+            member,
+            // colon_token,
+            value,
+        })
+    }
+}
+
+pub struct Args {
+    // advice: Ident,
+    // before: Ident,
+    // after: Ident,
+}
+
+impl Parse for Args {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let vars = Punctuated::<AdviceField, Token![,]>::parse_terminated(input)?;
+        let idents: Vec<AdviceField> = vars.into_iter().collect();
+        // dbg!(idents);
+        // todo!("GOOD")
+        Ok(Args {
+            // vars: vars.into_iter().collect(),
+        })
+    }
+}
diff --git a/macro/src/lib.rs b/macro/src/lib.rs
index 20ab885..8dfbe17 100644
--- a/macro/src/lib.rs
+++ b/macro/src/lib.rs
@@ -1,68 +1,406 @@
-#![allow(unused_imports, unused_variables, dead_code, non_snake_case)]
+#![allow(clippy::needless_doctest_main)]
+// #![warn(
+//     missing_debug_implementations,
+//     missing_docs,
+//     rust_2018_idioms,
+//     unreachable_pub
+// )]
+#![doc(test(
+    no_crate_inject,
+    attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
+))]
 
 use proc_macro::TokenStream;
-use quote::quote;
-use syn::{
-    parse::{Parse, ParseStream, Result},
-    parse_macro_input,
-    punctuated::Punctuated,
-    DeriveInput, Ident, ItemFn, Token,
-};
-
-struct AdviceField {
-    member: Ident,
-    // colon: Token![:],
-    value: syn::LitStr,
-}
-
-impl Parse for AdviceField {
-    fn parse(input: ParseStream) -> Result<Self> {
-        let member: Ident = input.parse()?;
-        let colon_token: Token![:] = input.parse()?;
-        // let value: Expr = input.parse()?;
-        let value: syn::LitStr = input.parse()?;
-
-        Ok(AdviceField {
-            member,
-            // colon_token,
-            value,
-        })
-    }
-}
-
-struct Args {
-    // advice: Ident,
-    // before: Ident,
-    // after: Ident,
-}
-
-impl Parse for Args {
-    fn parse(input: ParseStream) -> Result<Self> {
-        let vars = Punctuated::<AdviceField, Token![,]>::parse_terminated(input)?;
-        let idents: Vec<AdviceField> = vars.into_iter().collect();
-        // dbg!(idents);
-        // todo!("GOOD")
-        Ok(Args {
-            // vars: vars.into_iter().collect(),
-        })
-    }
+
+mod impls;
+use impls::{Mode, Type};
+
+const MAX_UDP_PAYLOAD: usize = 65507;
+
+/// Composition root for application entry point.
+///
+/// Inspired by `#[tokio::main]`
+///
+/// Implements dependency injection composition root pattern for
+/// application entry point.
+///
+/// ## Examples
+///
+/// ```
+/// #[ruex::main {
+///     backtrace = {
+///         level = Short,
+///     },
+///     log = {
+///         level = Info,
+///         output = Stdout,
+///     }
+/// }]
+/// fn main() {}
+/// ```
+///
+/// ## Config
+/// Configuration fields can be omitted.
+///
+/// ```rust,no_run
+/// struct MainConfig {
+///     backtrace: BacktraceConfig,
+///     log: LogConfig,
+/// }
+///
+/// struct LogConfig {
+///     level: LogLevel,
+///     output: LogOutput,
+/// }
+///
+/// enum LogOutput {
+///     #[default]
+///     StdOut,
+///     Syslog,
+///     File(String),
+/// }
+///
+/// enum LogLevel {
+///     Trace,
+///     Debug,
+///     #[default]
+///     Info,
+///     Warn,
+///     Error,
+/// }
+///
+/// struct BacktraceConfig {
+///     level: BactraceLevel,
+/// }
+///
+/// enum BactraceLevel {
+///     None,
+///     #[default]
+///     Short,
+///     Full,
+/// }
+/// ```
+#[proc_macro_attribute]
+// #[cfg(not(test))] // Work around for rust-lang/rust#62127
+pub fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
+    impls::main(attr, item)
 }
 
+/// Decorate methods.
+///
+/// Inspired by `deco`
+///
+/// ## Examples
+///
+/// ```
+/// use ruex::prelude::*;
+///
+/// fn logging<F>(func: F) -> impl Fn(i32) -> i32
+/// where
+///     F: Fn(i32) -> i32,
+/// {
+///     move |i| {
+///         println!("Input = {}", i);
+///         let out = func(i);
+///         println!("Output = {}", out);
+///         out
+///     }
+/// }
+///
+/// #[decorate(logging)]
+/// fn add2(i: i32) -> i32 {
+///     i + 2
+/// }
+///
+/// add2(2);
+/// ```
+///
+/// - Decorator with parameter
+///
+/// ```
+/// use ruex::prelude::*;
+/// use std::{fs, io::Write};
+///
+/// fn logging<InputFunc: 'static>(
+///     log_filename: &'static str,
+/// ) -> impl Fn(InputFunc) -> Box<dyn Fn(i32) -> i32>
+/// where
+///     InputFunc: Fn(i32) -> i32,
+/// {
+///     move |func: InputFunc| {
+///         Box::new(move |i: i32| {
+///             let mut f = fs::File::create(log_filename).unwrap();
+///             writeln!(f, "Input = {}", i).unwrap();
+///             let out = func(i);
+///             writeln!(f, "Output = {}", out).unwrap();
+///             out
+///         })
+///     }
+/// }
+///
+/// #[decorate(logging("test.log"))]
+/// fn add2(i: i32) -> i32 {
+///     i + 2
+/// }
+///
+/// add2(2);
+/// ```
+///
 #[proc_macro_attribute]
-pub fn Aspect(attr: TokenStream, item: TokenStream) -> TokenStream {
-    // println!("attr: \"{}\"", attr.to_string());
-    let attr = attr.clone();
-    let attr = syn::parse_macro_input!(attr as Args);
+pub fn decorate(attr: TokenStream, item: TokenStream) -> TokenStream {
+    impls::decorate(attr, item)
+}
 
-    // println!("item: \"{}\"", item.to_string());
+/// Delegate method to a field.
+///
+/// Inspired by delegate-attr
+///
+/// ## Examples
+///
+/// ### Delegate `impl` block
+///
+/// ```
+/// use delegate_attr::delegate;
+///
+/// struct Foo(String);
+///
+/// #[delegate(self.0)]
+/// impl Foo {
+///     fn as_str(&self) -> &str;
+///     fn into_bytes(self) -> Vec<u8>;
+/// }
+///
+/// let foo = Foo("hello".to_owned());
+/// assert_eq!(foo.as_str(), "hello");
+/// assert_eq!(foo.into_bytes(), b"hello");
+/// ```
+///
+/// ### Delegate trait `impl`
+///
+/// ```
+/// # use delegate_attr::delegate;
+///
+/// struct Iter(std::vec::IntoIter<u8>);
+///
+/// #[delegate(self.0)]
+/// impl Iterator for Iter {
+///     type Item = u8;
+///     fn next(&mut self) -> Option<u8>;
+///     fn count(self) -> usize;
+///     fn size_hint(&self) -> (usize, Option<usize>);
+///     fn last(self) -> Option<u8>;
+/// }
+///
+/// let iter = Iter(vec![1, 2, 4, 8].into_iter());
+/// assert_eq!(iter.count(), 4);
+/// let iter = Iter(vec![1, 2, 4, 8].into_iter());
+/// assert_eq!(iter.last(), Some(8));
+/// let iter = Iter(vec![1, 2, 4, 8].into_iter());
+/// assert_eq!(iter.sum::<u8>(), 15);
+/// ```
+///
+/// ### With more complicated target
+///
+/// ```
+/// # use delegate_attr::delegate;
+/// # use std::cell::RefCell;
+/// struct Foo<T> {
+///     inner: RefCell<Vec<T>>,
+/// }
+///
+/// #[delegate(self.inner.borrow())]
+/// impl<T> Foo<T> {
+///     fn len(&self) -> usize;
+/// }
+///
+/// #[delegate(self.inner.borrow_mut())]
+/// impl<T> Foo<T> {
+///     fn push(&self, value: T);
+/// }
+///
+/// #[delegate(self.inner.into_inner())]
+/// impl<T> Foo<T> {
+///     fn into_boxed_slice(self) -> Box<[T]>;
+/// }
+///
+/// let foo = Foo { inner: RefCell::new(vec![1]) };
+/// assert_eq!(foo.len(), 1);
+/// foo.push(2);
+/// assert_eq!(foo.len(), 2);
+/// assert_eq!(foo.into_boxed_slice().as_ref(), &[1, 2]);
+/// ```
+///
+/// ### `into` and `call` attribute
+///
+/// ```
+/// # use delegate_attr::delegate;
+/// struct Inner;
+/// impl Inner {
+///     pub fn method(&self, num: u32) -> u32 { num }
+/// }
+///
+/// struct Wrapper { inner: Inner }
+///
+/// #[delegate(self.inner)]
+/// impl Wrapper {
+///     // calls method, converts result to u64
+///     #[into]
+///     pub fn method(&self, num: u32) -> u64;
+///
+///     // calls method, returns ()
+///     #[call(method)]
+///     pub fn method_noreturn(&self, num: u32);
+/// }
+/// ```
+///
+/// ### Delegate single method
+///
+/// ```
+/// # use delegate_attr::delegate;
+/// struct Foo<T>(Vec<T>);
+///
+/// impl<T> Foo<T> {
+///     #[delegate(self.0)]
+///     fn len(&self) -> usize;
+/// }
+///
+/// let foo = Foo(vec![1]);
+/// assert_eq!(foo.len(), 1);
+/// ```
+#[proc_macro_attribute]
+pub fn delegate(attr: TokenStream, item: TokenStream) -> TokenStream {
+    impls::delegate(attr, item)
+}
 
-    item
+/// Elegant trait mocking.
+///
+/// Use it with #[cfg_attr(test, mock)]
+///
+/// ## Examples
+///
+/// ```rust
+/// #![allow(unused_variables)]
+/// use ruex::prelude::*;
+///
+/// /// Here
+/// #[mock]
+/// trait Nurse {
+///     fn heal(&self, value: i32, direction: i32) -> i32 {
+///         0
+///     }
+///
+///     fn leave(&self, value: i32) -> i32 {
+///         0
+///     }
+/// }
+///
+/// #[derive(Default)]
+/// struct Foo;
+///
+/// impl Nurse for Foo {
+///     fn heal(&self, value: i32, direction: i32) -> i32 {
+///         25
+///     }
+///
+///     fn leave(&self, value: i32) -> i32 {
+///         31
+///     }
+/// }
+///
+/// fn main() {
+///     let nurse = Foo::mock().heal(|value, direction| 123).build();
+///
+///     let val = nurse.heal(23, 0);
+///     println!("VALUE: {val}");
+///
+///     let val = nurse.leave(23);
+///     println!("VALUE: {val}");
+/// }
+/// ```
+#[proc_macro_attribute]
+pub fn mock(attr: TokenStream, item: TokenStream) -> TokenStream {
+    impls::mock(attr, item)
+}
+
+/// Subject registration.
+#[proc_macro_attribute]
+pub fn register(attr: TokenStream, item: TokenStream) -> TokenStream {
+    impls::register(attr.into(), item.into()).into()
 }
 
-#[cfg(test)]
-mod tests {
-    #[test]
-    fn it_works() {
-        assert_eq!(2 + 2, 4);
-    }
+/// Aspect-oriented methodology.
+///
+#[proc_macro_attribute]
+pub fn aspect(attr: TokenStream, item: TokenStream) -> TokenStream {
+    impls::contracts_aspect(Type::Aspect, Mode::Always, attr.into(), item.into()).into()
+}
+
+/// Composition pattern implementation.
+///
+#[proc_macro_derive(Compose, attributes(delegate))]
+pub fn derive_compose(item: TokenStream) -> TokenStream {
+    impls::compose(item.into()).into()
+}
+
+/// Debug ensures for contracts.
+///
+#[proc_macro_attribute]
+pub fn debug_ensures(attr: TokenStream, item: TokenStream) -> TokenStream {
+    impls::contracts_aspect(Type::Ensures, Mode::Debug, attr.into(), item.into()).into()
+}
+
+/// Debug invariant for contracts.
+///
+#[proc_macro_attribute]
+pub fn debug_invariant(attr: TokenStream, item: TokenStream) -> TokenStream {
+    impls::contracts_aspect(Type::Invariant, Mode::Debug, attr.into(), item.into()).into()
+}
+
+/// Debug requires for contracts.
+///
+#[proc_macro_attribute]
+pub fn debug_requires(attr: TokenStream, item: TokenStream) -> TokenStream {
+    impls::contracts_aspect(Type::Requires, Mode::Debug, attr.into(), item.into()).into()
+}
+
+/// Ensures for contracts.
+///
+#[proc_macro_attribute]
+pub fn ensures(attr: TokenStream, item: TokenStream) -> TokenStream {
+    impls::contracts_aspect(Type::Ensures, Mode::Always, attr.into(), item.into()).into()
+}
+
+/// Invariant for contracts.
+///
+#[proc_macro_attribute]
+pub fn invariant(attr: TokenStream, item: TokenStream) -> TokenStream {
+    impls::contracts_aspect(Type::Invariant, Mode::Always, attr.into(), item.into()).into()
+}
+
+/// Requires for contracts.
+///
+#[proc_macro_attribute]
+pub fn requires(attr: TokenStream, item: TokenStream) -> TokenStream {
+    impls::contracts_aspect(Type::Requires, Mode::Always, attr.into(), item.into()).into()
+}
+
+/// Test ensures for contracts.
+///
+#[proc_macro_attribute]
+pub fn test_ensures(attr: TokenStream, item: TokenStream) -> TokenStream {
+    impls::contracts_aspect(Type::Ensures, Mode::Test, attr.into(), item.into()).into()
+}
+
+/// Test invariant for contracts.
+///
+#[proc_macro_attribute]
+pub fn test_invariant(attr: TokenStream, item: TokenStream) -> TokenStream {
+    impls::contracts_aspect(Type::Invariant, Mode::Test, attr.into(), item.into()).into()
+}
+
+/// Test requires for contracts.
+///
+#[proc_macro_attribute]
+pub fn test_requires(attr: TokenStream, item: TokenStream) -> TokenStream {
+    impls::contracts_aspect(Type::Requires, Mode::Test, attr.into(), item.into()).into()
 }
diff --git a/src/foundation/patterns/default/controller.rs b/src/foundation/patterns/default/controller.rs
index 54d7af8..a842632 100644
--- a/src/foundation/patterns/default/controller.rs
+++ b/src/foundation/patterns/default/controller.rs
@@ -86,10 +86,10 @@ where
 
         log::info!("Execute Command [BaseController] {:?}", notification);
 
-        command_map.get(&notification.interest()).map(|command| {
+        if let Some(command) = command_map.get(&notification.interest()) {
             log::info!("Command [BaseController] {:?} for {:?}", command, notification);
             command.execute(notification)
-        });
+        }
     }
 
     fn has_command(&self, interest: &Interest) -> bool {
diff --git a/src/foundation/patterns/default/model.rs b/src/foundation/patterns/default/model.rs
index 2ee5847..f738df3 100644
--- a/src/foundation/patterns/default/model.rs
+++ b/src/foundation/patterns/default/model.rs
@@ -24,6 +24,9 @@ use crate::prelude::{Model, Proxy, Singleton};
 /// 
 /// [Command]: crate::prelude::Command
 /// [Facade]: crate::prelude::Facade
+/// 
+
+#[derive(Default)]
 pub struct BaseModel {
     // Mapping of proxy types to [Proxy] instances
     storages: RefCell<BTreeMap<TypeId, Rc<dyn Any>>>,
diff --git a/src/foundation/patterns/default/view.rs b/src/foundation/patterns/default/view.rs
index 39fbac0..ae52d63 100644
--- a/src/foundation/patterns/default/view.rs
+++ b/src/foundation/patterns/default/view.rs
@@ -8,7 +8,10 @@ use std::{
 
 use crate::{
     foundation::patterns::observer::BaseObserver,
-    prelude::{Interest, Mediator, MediatorRegistry, Notification, NotifyContext, Observer, Singleton, View},
+    prelude::{
+        Interest, Mediator, MediatorRegistry, Notification, NotifyContext, Observer, Singleton,
+        View,
+    },
 };
 
 /// A Singleton [View] implementation.
@@ -81,12 +84,7 @@ where
         // Copy observers from reference array to working array,
         // since the reference array may change during the notification loop
         // and prevent double borrow ))
-        let observers = {
-            self.observer_map
-                .borrow()
-                .get(&note.interest())
-                .map(|observers| observers.clone())
-        };
+        let observers = self.observer_map.borrow().get(&note.interest()).cloned();
 
         if let Some(observers) = observers {
             for observer in observers.iter() {
@@ -100,13 +98,11 @@ where
         // log::info!("Register Observer [BaseView] {:?}", interest);
         let mut observer_map = self.observer_map.borrow_mut();
 
-        if !observer_map.contains_key(&interest) {
-            observer_map.insert(interest.clone(), Vec::new());
-        }
+        observer_map.entry(interest).or_insert_with(Vec::new);
 
-        observer_map
-            .get_mut(&interest)
-            .map(|observers| observers.push(observer));
+        if let Some(observers) = observer_map.get_mut(&interest) {
+            observers.push(observer)
+        }
     }
 
     // It private so its fun
@@ -114,17 +110,17 @@ where
         let mut observer_map = self.observer_map.borrow_mut();
 
         // the observer list for the notification under inspection
-        observer_map.remove(interest).as_mut().map(|observers| {
+        if let Some(observers) = observer_map.remove(interest).as_mut() {
             // find the observer for the notify_context
             for (idx, observer) in observers.iter().enumerate() {
-                if observer.compare_context(context) == true {
+                if observer.compare_context(context) {
                     // there can only be one Observer for a given notify_context
                     // in any given Observer list, so remove it and break
                     observers.remove(idx);
                     break;
                 }
             }
-        });
+        }
     }
 }
 
@@ -148,7 +144,7 @@ where
 
         // Get Notification interests, if any.
         let interests = mediator.list_notification_interests();
-        if interests.len() > 0 {
+        if !interests.is_empty() {
             let mediator = mediator.clone();
             let context = mediator.clone();
             // Create Observer
@@ -162,7 +158,7 @@ where
 
             // Register Mediator as Observer for its list of Notification interests
             for interest in interests.iter() {
-                self.register_observer(interest.clone(), observer.clone());
+                self.register_observer(*interest, observer.clone());
             }
         }
 
@@ -174,7 +170,7 @@ where
 
         match self.mediator_map.borrow().get(&type_id) {
             Some(item) => match item.clone().downcast::<M>() {
-                Ok(mediator) => Some(mediator.clone()),
+                Ok(mediator) => Some(mediator),
                 Err(_) => {
                     log::error!("Something wrong with proxy storage");
                     None
@@ -188,42 +184,45 @@ where
         // remove the mediator from the map
         let type_id = TypeId::of::<M>();
 
-        self.mediator_map.borrow_mut().remove(&type_id).map(|mediator| {
-            match mediator.downcast::<M>() {
-                Ok(mediator) => {
-                    // for every notification this mediator is interested in...
-                    let interests = mediator.list_notification_interests();
-                    for interest in interests.iter() {
-                        // remove the observer linking the mediator
-                        // to the notification interest
-
-                        let mut observer_map = self.observer_map.borrow_mut();
-
-                        let context = mediator.id();
-
-                        // the observer list for the notification under inspection
-                        observer_map.remove(interest).as_mut().map(|observers| {
-                            // find the observer for the notify_context
-                            for (idx, observer) in observers.iter().enumerate() {
-                                if observer.context().id() == context {
-                                    // there can only be one Observer for a given notify_context
-                                    // in any given Observer list, so remove it and break
-                                    observers.remove(idx);
-                                    break;
+        self.mediator_map
+            .borrow_mut()
+            .remove(&type_id)
+            .map(|mediator| {
+                match mediator.downcast::<M>() {
+                    Ok(mediator) => {
+                        // for every notification this mediator is interested in...
+                        let interests = mediator.list_notification_interests();
+                        for interest in interests.iter() {
+                            // remove the observer linking the mediator
+                            // to the notification interest
+
+                            let mut observer_map = self.observer_map.borrow_mut();
+
+                            let context = mediator.id();
+
+                            // the observer list for the notification under inspection
+                            if let Some(observers) = observer_map.remove(interest).as_mut() {
+                                // find the observer for the notify_context
+                                for (idx, observer) in observers.iter().enumerate() {
+                                    if observer.context().id() == context {
+                                        // there can only be one Observer for a given notify_context
+                                        // in any given Observer list, so remove it and break
+                                        observers.remove(idx);
+                                        break;
+                                    }
                                 }
                             }
-                        });
-                    }
+                        }
 
-                    // alert the mediator that it has been removed
-                    mediator.on_remove();
-                    mediator
-                }
-                Err(_) => {
-                    panic!("Something wrong with mediator storage");
+                        // alert the mediator that it has been removed
+                        mediator.on_remove();
+                        mediator
+                    }
+                    Err(_) => {
+                        panic!("Something wrong with mediator storage");
+                    }
                 }
-            }
-        })
+            })
     }
 
     fn has_mediator<M: Mediator<Body>>(&self) -> bool {
diff --git a/src/foundation/patterns/fsm/integrations.rs b/src/foundation/patterns/fsm/integrations.rs
index 49f886b..29bfbcf 100644
--- a/src/foundation/patterns/fsm/integrations.rs
+++ b/src/foundation/patterns/fsm/integrations.rs
@@ -18,11 +18,17 @@ pub struct CallbackIntegration;
 
 impl CallbackIntegration {
     /// Create new callback integration
-    pub fn new() {}
+    pub fn new() -> Self {
+        Self
+    }
 }
 
 impl FsmIntegration<Self> for CallbackIntegration {
-    fn transition(&self, new_state: Rc<StateDef<Self>>, old_state: Option<Rc<StateDef<Self>>>) -> bool {
+    fn transition(
+        &self,
+        new_state: Rc<StateDef<Self>>,
+        old_state: Option<Rc<StateDef<Self>>>,
+    ) -> bool {
         if let Some(ref old_state) = old_state {
             old_state.state.exit(self)
         }
diff --git a/src/foundation/patterns/mediator/mediator.rs b/src/foundation/patterns/mediator/mediator.rs
index 40f51f5..a91feb3 100644
--- a/src/foundation/patterns/mediator/mediator.rs
+++ b/src/foundation/patterns/mediator/mediator.rs
@@ -30,7 +30,7 @@ where
     Body: fmt::Debug + 'static,
 {
     fn view_component(&self) -> Option<Rc<dyn View<Body>>> {
-        self.view_component.as_ref().map(|c| c.clone())
+        self.view_component.as_ref().cloned()
     }
 
     fn handle_notification(&self, _notification: Rc<dyn Notification<Body>>) {}
diff --git a/src/foundation/patterns/mod.rs b/src/foundation/patterns/mod.rs
index a222283..c29257c 100644
--- a/src/foundation/patterns/mod.rs
+++ b/src/foundation/patterns/mod.rs
@@ -1,5 +1,11 @@
 //! Catalog of patterns
 
+#![allow(
+    clippy::module_inception,
+    clippy::new_without_default,
+    clippy::type_complexity
+)]
+
 pub mod command;
 
 pub mod default;
@@ -14,4 +20,4 @@ pub mod observer;
 
 pub mod proxy;
 
-pub mod builder;
\ No newline at end of file
+pub mod builder;
diff --git a/src/foundation/patterns/observer/notifier.rs b/src/foundation/patterns/observer/notifier.rs
index d90d8ae..7d2bdf0 100644
--- a/src/foundation/patterns/observer/notifier.rs
+++ b/src/foundation/patterns/observer/notifier.rs
@@ -28,6 +28,7 @@ use crate::{
 /// [Proxy]: crate::prelude::Proxy
 /// [Facade]: crate::prelude::Facade
 
+#[derive(Default)]
 pub struct BaseNotifier;
 
 impl BaseNotifier {
diff --git a/src/lib.rs b/src/lib.rs
index 2300b36..9e0806c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,34 +1,37 @@
 #![doc(html_logo_url = "https://dudochkin-victor.github.io/assets/ruex/logo.svg")]
-
 #![warn(missing_docs)]
 
-//! Design pattern framework on top of PureMVC. 
-//! 
+//! Design pattern framework on top of PureMVC.
+//!
 //! The PureMVC framework has a very narrow goal. That is to help you
 //! separate your application’s coding interests into three discrete tiers:
 //! [Model][2], [View][3] and [Controller][1].
-//! 
+//!
 //! This separation of interests, and the tightness and direction of the
 //! couplings used to make them work together is of paramount
 //! importance in the building of scalable and maintainable applications.
-//! 
+//!
 //! In this implementation of the classic MVC Design meta-pattern, these
 //! three tiers of the application are governed by three Singletons (a class
 //! where only one instance may be created) called simply [Model][2], [View][3]
 //! and [Controller][1]. Together, they are referred to as the ‘Core actors’.
-//! 
+//!
 //! A fourth Singleton, the [Facade][4] simplifies development by providing a
 //! single interface for communication with the Core actors.
-//! 
+//!
 //! [Read more..][foundation]
-//! 
+//!
 //! ![PureMVC Diagram](https://raw.githubusercontent.com/wiki/ohyo-io/wampire/images/pure-mvc.svg)
-//! 
+//!
 //! [1]: crate::prelude::Controller
 //! [2]: crate::prelude::Model
 //! [3]: crate::prelude::View
 //! [4]: crate::prelude::Facade
-//! 
+//!
 pub mod foundation;
 
 pub mod prelude;
+
+pub mod utils;
+
+pub use ruex_macro::main;
diff --git a/src/prelude/compose.rs b/src/prelude/compose.rs
new file mode 100644
index 0000000..d3a3178
--- /dev/null
+++ b/src/prelude/compose.rs
@@ -0,0 +1,28 @@
+/// Function composition
+///
+/// Allows to compose functions and closures.
+///
+/// ```no_run
+/// let chained = checkit
+///   .chain(add_data)
+///   .chain(add_experiments)
+///   .chain(add_data)
+///   .chain(|inout| format!("{input}/README.md"));
+/// ```
+pub trait Compose<In, Out>: Fn(In) -> Out {
+    /// `impl Trait` only allowed in function and inherent method return types,
+    /// not in trait method return - so its boxed
+    fn chain<Ret>(self, next: impl Fn(Out) -> Ret + 'static) -> Box<dyn Fn(In) -> Ret>;
+}
+
+impl<F, In, Out> Compose<In, Out> for F
+where
+    F: Fn(In) -> Out + 'static,
+{
+    fn chain<Ret>(self, next: impl Fn(Out) -> Ret + 'static) -> Box<dyn Fn(In) -> Ret> {
+        Box::new(move |args: In| {
+            // chained
+            next(self(args))
+        })
+    }
+}
diff --git a/src/prelude/currying.rs b/src/prelude/currying.rs
new file mode 100644
index 0000000..87320b9
--- /dev/null
+++ b/src/prelude/currying.rs
@@ -0,0 +1,126 @@
+//! Functions with lots of parameters are considered bad style and
+//! reduce readability (“what does the 5th parameter mean?”).
+//! Consider grouping some parameters into a new type.
+
+/// Currying for functions with 2 params
+///
+/// ```
+/// fn two_params(a1: &str, a2: &str) -> String {
+///     format!("{a1}/{a2}")
+/// }
+///
+/// let one = two_params.curry("/mnt");
+/// println!("{}", one("home"));
+/// ```
+pub trait Curry<A, B> {
+    /// The concrete type that `curry` returns.
+    type Output;
+    /// Curry this function, transforming it from
+    ///
+    /// `fn(A, B) -> R`
+    /// to
+    /// `fn(A) -> fn(B) -> R`
+    fn curry(self, a: A) -> Self::Output;
+}
+
+impl<Func, A, B, Out> Curry<A, B> for Func
+where
+    Func: Fn(A, B) -> Out + 'static,
+    A: Copy + 'static,
+{
+    type Output = Box<dyn Fn(B) -> Out>;
+
+    fn curry(self, a: A) -> Self::Output {
+        Box::new(move |b: B| self(a, b))
+    }
+}
+
+// not working magic !!! )))
+// impl<Func, A, B> FnOnce<(A,)> for Func
+// where
+//     Func: Curry<A, B>,
+// {
+//     type Output = <Func as Curry<A, B>>::Output;
+//     extern "rust-call" fn call_once(self, args: (A,)) -> Self::Output {
+//         self.curry(args.0)
+//     }
+// }
+
+/// Currying for functions with 3 params.
+///
+/// See [Curry] for details.
+
+pub trait Curry2<A, B, C> {
+    /// The concrete type that `curry` returns.
+    type Output;
+    /// Curry this function, transforming it from
+    ///
+    /// `fn(A, B, C) -> R`
+    /// to
+    /// `fn(A) -> fn(B, C) -> R`
+    fn curry(self, a: A) -> Self::Output;
+}
+
+impl<Func, A, B, C, Out> Curry2<A, B, C> for Func
+where
+    Func: Fn(A, B, C) -> Out + 'static,
+    A: Copy + 'static,
+{
+    type Output = Box<dyn Fn(B, C) -> Out>;
+
+    fn curry(self, a: A) -> Self::Output {
+        Box::new(move |b: B, c: C| self(a, b, c))
+    }
+}
+
+/// Currying for functions with 4 params
+///
+/// See [Curry] for details.
+pub trait Curry3<A, B, C, D> {
+    /// The concrete type that `curry` returns.
+    type Output;
+    /// Curry this function, transforming it from
+    ///
+    /// `fn(A, B, C, D) -> R`
+    /// to
+    /// `fn(A) -> fn(B, C, D) -> R`
+    fn curry(self, a: A) -> Self::Output;
+}
+
+impl<Func, A, B, C, D, Out> Curry3<A, B, C, D> for Func
+where
+    Func: Fn(A, B, C, D) -> Out + 'static,
+    A: Copy + 'static,
+{
+    type Output = Box<dyn Fn(B, C, D) -> Out>;
+
+    fn curry(self, a: A) -> Self::Output {
+        Box::new(move |b: B, c: C, d: D| self(a, b, c, d))
+    }
+}
+
+/// Currying for functions with 5 params
+///
+/// See [Curry] for details.
+pub trait Curry4<A, B, C, D, E> {
+    /// The concrete type that `curry` returns.
+    type Output;
+    /// Curry this function, transforming it from
+    ///
+    /// `fn(A, B, C, D, E) -> R`
+    /// to
+    /// `fn(A) -> fn(B, C, D, E) -> R`
+    fn curry(self, a: A) -> Self::Output;
+}
+
+impl<Func, A, B, C, D, E, Out> Curry4<A, B, C, D, E> for Func
+where
+    Func: Fn(A, B, C, D, E) -> Out + 'static,
+    A: Copy + 'static,
+{
+    type Output = Box<dyn Fn(B, C, D, E) -> Out>;
+
+    fn curry(self, a: A) -> Self::Output {
+        Box::new(move |b: B, c: C, d: D, e: E| self(a, b, c, d, e))
+    }
+}
diff --git a/src/prelude/mod.rs b/src/prelude/mod.rs
index d77d70d..f4d3713 100644
--- a/src/prelude/mod.rs
+++ b/src/prelude/mod.rs
@@ -14,9 +14,15 @@ pub use self::builder::*;
 mod command;
 pub use self::command::*;
 
+mod compose;
+pub use self::compose::*;
+
 mod controller;
 pub use self::controller::*;
 
+mod currying;
+pub use self::currying::*;
+
 mod facade;
 pub use self::facade::*;
 
@@ -32,6 +38,9 @@ pub use self::notification::*;
 mod notifier;
 pub use self::notifier::*;
 
+// mod pipe;
+// pub use self::pipe::*;
+
 mod observer;
 pub use self::observer::*;
 
@@ -43,3 +52,20 @@ pub use self::singleton::*;
 
 mod view;
 pub use self::view::*;
+
+pub use crate::utils::*;
+
+// pub use ruex_macro::aspect;
+// pub use ruex_macro::decorate;
+// pub use ruex_macro::delegate;
+// pub use ruex_macro::register;
+// pub use ruex_macro::Compose;
+pub use ruex_macro::*;
+
+/// Joint point stub for aspect development
+pub struct AspectJointPoint;
+
+impl AspectJointPoint {
+    /// Marker for joint point
+    pub fn proceed() {}
+}
diff --git a/src/prelude/pipe.rs b/src/prelude/pipe.rs
new file mode 100644
index 0000000..ccca345
--- /dev/null
+++ b/src/prelude/pipe.rs
@@ -0,0 +1,470 @@
+/// Helper trait to call free functions using method call syntax.
+///
+/// Rust already allows calling methods using function call syntax, but not the other way around.
+/// This crate fills the gap by providing a simple helper trait `Pipe`.
+/// 
+/// Inspired by https://github.com/xzfc/ufcs.rs, https://github.com/ear7h/plumb
+/// and borrowed from https://github.com/KSXGitHub/pipe-trait.git
+///
+/// ## See also
+///
+/// Roughtly the same feature is either implemented or proposed in various languages.
+///
+/// ### Rust
+///
+/// * [`pipeline.rs`](https://github.com/johannhof/pipeline.rs): implemented as an macro
+/// * [RFC #289](https://github.com/rust-lang/rfcs/issues/289): Unified function / method call syntax
+/// * [RFC #2049](https://github.com/rust-lang/rfcs/issues/2049): Piping data to functions
+/// * [Method-cascading and pipe-forward operators proposal](https://internals.rust-lang.org/t/method-cascading-and-pipe-forward-operators-proposal/7384/59)
+/// * [Rust #44874](https://github.com/rust-lang/rust/issues/44874): Tracking issue for `arbitrary_self_types`
+///
+/// ### Other languages
+///
+/// * https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax
+/// * [Nim], [DLang]: built-in UFCS
+/// * F#, [Elixir]: the pipe operator `|>`
+/// * C++: [proposed](https://brevzin.github.io/c++/2019/04/13/ufcs-history/)
+/// * [Nim]: https://nim-lang.org/docs/manual.html#procedures-method-call-syntax
+/// * [DLang]: https://tour.dlang.org/tour/en/gems/uniform-function-call-syntax-ufcs
+/// * [Elixir]: https://elixirschool.com/en/lessons/basics/pipe-operator/
+/// 
+/// Make it possible to chain regular functions.
+///
+/// **API Overview:**
+///
+/// By adding `use ruex::prelude::*`, 9 methods are added to all types:
+///
+/// |        identifier       |       pipe syntax      |  traditional syntax |
+/// |:-----------------------:|:----------------------:|:-------------------:|
+/// | `Pipe::pipe`            | `x.pipe(f)`            | `f(x)`              |
+/// | `Pipe::pipe_ref`        | `x.pipe_ref(f)`        | `f(&x)`             |
+/// | `Pipe::pipe_mut`        | `x.pipe_mut(f)`        | `f(&mut x)`         |
+/// | `Pipe::pipe_as_ref`     | `x.pipe_as_ref(f)`     | `f(x.as_ref())`     |
+/// | `Pipe::pipe_as_mut`     | `x.pipe_as_mut(f)`     | `f(x.as_mut())`     |
+/// | `Pipe::pipe_deref`      | `x.pipe_deref(f)`      | `f(&x)`             |
+/// | `Pipe::pipe_deref_mut`  | `x.pipe_deref_mut(f)`  | `f(&mut x)`         |
+/// | `Pipe::pipe_borrow`     | `x.pipe_borrow(f)`     | `f(x.borrow())`     |
+/// | `Pipe::pipe_borrow_mut` | `x.pipe_borrow_mut(f)` | `f(x.borrow_mut())` |
+///
+/// **Example:** Same type
+///
+/// ```rust
+/// use ruex::prelude::*;
+/// let inc = |x| x + 1;
+/// let double = |x| x + x;
+/// let square = |x| x * x;
+/// let a = (123i32).pipe(inc).pipe(double).pipe(square);
+/// let b = square(double(inc(123i32)));
+/// assert_eq!(a, b);
+/// ```
+///
+/// **Example:** Type transformation
+///
+/// ```rust
+/// use ruex::prelude::*;
+/// let x = 'x';
+/// let a = x
+///     .pipe(|x| (x, x, x)) // (char, char, char)
+///     .pipe(|x| [x, x]) // [(char, char, char); 2]
+///     .pipe(|x| format!("{:?}", x)); // String
+/// let b = "[('x', 'x', 'x'), ('x', 'x', 'x')]";
+/// assert_eq!(a, b);
+/// ```
+///
+/// **Example:** Pipe amongst method chain
+///
+/// ```rust
+/// # async {
+/// # use std::fmt::*;
+/// # use futures::future::*;
+/// # #[derive(Debug, Copy, Clone)]
+/// # struct Num(pub i32);
+/// # impl Num {
+/// #     pub fn inc(&self) -> Self { Self(self.0 + 1) }
+/// #     pub fn double(&self) -> Self { Self(self.0 * 2) }
+/// #     pub fn square(&self) -> Self { Self(self.0 * self.0) }
+/// #     pub fn get(&self) -> i32 { self.0 }
+/// #     pub fn future(self) -> Ready<Self> { ready(self) }
+/// # }
+/// # let my_future = Num(12).future();
+/// use ruex::prelude::*;
+/// fn log<X: Debug>(x: X) -> X {
+///     println!("value: {:?}", x);
+///     x
+/// }
+/// my_future
+///     .pipe(log)
+///     .await
+///     .pipe(log)
+///     .inc()
+///     .pipe(log)
+///     .double()
+///     .pipe(log)
+///     .square()
+///     .pipe(log)
+///     .get()
+///     .pipe(log);
+/// # };
+/// ```
+///
+/// **Example:** Explicit type annotation
+///
+/// ```rust
+/// use ruex::prelude::*;
+/// let x = "abc".to_string();
+/// let a = x
+///     .pipe_ref::<&str, _>(AsRef::as_ref)
+///     .chars()
+///     .pipe::<Box<_>, _>(Box::new)
+///     .collect::<Vec<_>>();
+/// let b = vec!['a', 'b', 'c'];
+/// assert_eq!(a, b);
+/// ```
+// #![no_std]
+use std::{
+    borrow::{Borrow, BorrowMut},
+    ops::{Deref, DerefMut},
+};
+
+/// All sized types implement this trait.
+pub trait Pipe {
+    /// Apply `f` to `self`.
+    ///
+    /// ```
+    /// # #[derive(Debug, PartialEq, Eq)]
+    /// # struct Foo(i32);
+    /// # fn double(x: i32) -> i32 { x * 2 }
+    /// # use ruex::prelude::*;
+    /// assert_eq!(
+    ///     12.pipe(double).pipe(Foo),
+    ///     Foo(double(12)),
+    /// )
+    /// ```
+    #[inline]
+    fn pipe<R, F>(self, f: F) -> R
+    where
+        Self: Sized,
+        F: FnOnce(Self) -> R,
+    {
+        f(self)
+    }
+
+    /// Apply `f` to `&self`.
+    ///
+    /// ```
+    /// # use ruex::prelude::*;
+    /// #[derive(Debug, PartialEq, Eq)]
+    /// struct Foo(i32);
+    /// let a = Foo(12);
+    /// let b = a
+    ///     .pipe_ref(|a| a.0) // a is not moved
+    ///     .pipe(Foo);
+    /// assert_eq!(a, b); // a is used again
+    /// ```
+    #[inline]
+    fn pipe_ref<'a, R, F>(&'a self, f: F) -> R
+    where
+        F: FnOnce(&'a Self) -> R,
+    {
+        f(self)
+    }
+
+    /// Apply `f` to `&mut self`.
+    ///
+    /// ```
+    /// # use ruex::prelude::*;
+    /// #[derive(Debug, PartialEq, Eq)]
+    /// struct Foo(i32, i32);
+    /// let mut a = Foo(0, 0);
+    /// a.pipe_mut(|a| a.0 = 12);
+    /// a.pipe_mut(|a| a.1 = 34);
+    /// assert_eq!(a, Foo(12, 34));
+    /// ```
+    #[inline]
+    fn pipe_mut<'a, R, F>(&'a mut self, f: F) -> R
+    where
+        F: FnOnce(&'a mut Self) -> R,
+    {
+        f(self)
+    }
+
+    /// Apply `f` to `&self` where `f` takes a single parameter of type `Param`
+    /// and `Self` implements trait [`AsRef<Param>`].
+    ///
+    /// ```
+    /// # use ruex::prelude::*;
+    /// fn uppercase(x: &str) -> String {
+    ///   x.to_uppercase()
+    /// }
+    /// let x: String = "abc".to_string();
+    /// let y: String = x.pipe_as_ref(uppercase);
+    /// assert_eq!(y, "ABC");
+    /// ```
+    #[inline]
+    fn pipe_as_ref<'a, P, R, F>(&'a self, f: F) -> R
+    where
+        Self: AsRef<P>,
+        P: ?Sized + 'a,
+        F: FnOnce(&'a P) -> R,
+    {
+        f(self.as_ref())
+    }
+
+    /// Apply `f` to `&mut self` where `f` takes a single parameter of type `Param`
+    /// and `Self` implements trait [`AsMut<Param>`].
+    ///
+    /// ```
+    /// # use ruex::prelude::*;
+    /// fn modify(target: &mut [i32]) {
+    ///   target[0] = 123;
+    /// }
+    /// let mut vec: Vec<i32> = vec![0, 1, 2, 3];
+    /// vec.pipe_as_mut(modify);
+    /// assert_eq!(vec, vec![123, 1, 2, 3]);
+    /// ```
+    #[inline]
+    fn pipe_as_mut<'a, P, R, F>(&'a mut self, f: F) -> R
+    where
+        Self: AsMut<P>,
+        P: ?Sized + 'a,
+        F: FnOnce(&'a mut P) -> R,
+    {
+        f(self.as_mut())
+    }
+
+    /// Apply `f` to `&self` where `f` takes a single parameter of type `Param`
+    /// and `Self` implements trait `Deref<Target = Param>`.
+    ///
+    /// ```
+    /// # use ruex::prelude::*;
+    /// fn uppercase(x: &str) -> String {
+    ///   x.to_uppercase()
+    /// }
+    /// let x: String = "abc".to_string();
+    /// let y: String = x.pipe_deref(uppercase);
+    /// assert_eq!(y, "ABC");
+    /// ```
+    #[inline]
+    fn pipe_deref<'a, Param, R, F>(&'a self, f: F) -> R
+    where
+        Self: Deref<Target = Param>,
+        Param: ?Sized + 'a,
+        F: FnOnce(&'a Param) -> R,
+    {
+        f(self)
+    }
+
+    /// Apply `f` to `&mut self` where `f` takes a single parameter of type `Param`
+    /// and `Self` implements trait [`DerefMut<Target = Param>`].
+    ///
+    /// ```
+    /// # use ruex::prelude::*;
+    /// fn modify(target: &mut [i32]) {
+    ///   target[0] = 123;
+    /// }
+    /// let mut vec: Vec<i32> = vec![0, 1, 2, 3];
+    /// vec.pipe_deref_mut(modify);
+    /// assert_eq!(vec, vec![123, 1, 2, 3]);
+    /// ```
+    #[inline]
+    fn pipe_deref_mut<'a, Param, R, F>(&'a mut self, f: F) -> R
+    where
+        Self: DerefMut<Target = Param>,
+        Param: ?Sized + 'a,
+        F: FnOnce(&'a mut Param) -> R,
+    {
+        f(self)
+    }
+
+    /// Apply `f` to `&self` where `f` takes a single parameter of type `Param`
+    /// and `Self` implements trait [`Borrow<Param>`].
+    ///
+    /// ```
+    /// # use ruex::prelude::*;
+    /// fn uppercase(x: &str) -> String {
+    ///   x.to_uppercase()
+    /// }
+    /// let x: String = "abc".to_string();
+    /// let y: String = x.pipe_borrow(uppercase);
+    /// assert_eq!(y, "ABC");
+    /// ```
+    #[inline]
+    fn pipe_borrow<'a, Param, R, F>(&'a self, f: F) -> R
+    where
+        Self: Borrow<Param>,
+        Param: ?Sized + 'a,
+        F: FnOnce(&'a Param) -> R,
+    {
+        f(self.borrow())
+    }
+
+    /// Apply `f` to `&mut self` where `f` takes a single parameter of type `Param`
+    /// and `Self` implements trait [`BorrowMut<Param>`].
+    ///
+    /// ```
+    /// # use ruex::prelude::*;
+    /// fn modify(target: &mut [i32]) {
+    ///   target[0] = 123;
+    /// }
+    /// let mut vec: Vec<i32> = vec![0, 1, 2, 3];
+    /// vec.pipe_borrow_mut(modify);
+    /// assert_eq!(vec, vec![123, 1, 2, 3]);
+    /// ```
+    #[inline]
+    fn pipe_borrow_mut<'a, Param, R, F>(&'a mut self, f: F) -> R
+    where
+        Self: BorrowMut<Param>,
+        Param: ?Sized + 'a,
+        F: FnOnce(&'a mut Param) -> R,
+    {
+        f(self.borrow_mut())
+    }
+}
+impl<T> Pipe for T {}
+
+#[cfg(test)]
+mod tests {
+    use futures::executor::block_on;
+    use futures::future::lazy;
+
+    use super::Pipe;
+
+    async fn async_fn(s: String) -> String {
+        lazy(|_| format!("a({})", s)).await
+    }
+
+    fn result_fn(s: String) -> Result<String, ()> {
+        Ok(format!("r({})", s))
+    }
+
+    #[test]
+    fn simple() {
+        assert_eq!("foo".pipe(Some), Some("foo"));
+    }
+
+    #[test]
+    fn chaining() {
+        let a: Result<String, ()> = block_on(async {
+            "foo"
+                .to_string()
+                .pipe(result_fn)?
+                .pipe(|x| format!("c({})", x))
+                .pipe(async_fn)
+                .await
+                .replace("f", "b")
+                .pipe(Ok)
+        });
+
+        let b: Result<String, ()> = block_on(async {
+            Ok(async_fn(format!("c({})", result_fn("foo".to_string())?))
+                .await
+                .replace("f", "b"))
+        });
+
+        assert_eq!(a, b);
+        assert_eq!(a, Ok(String::from("a(c(r(boo)))")));
+    }
+
+    #[test]
+    fn same_type() {
+        let x: i32 = 3;
+        let inc = |x| x + 1;
+        let double = |x| x + x;
+        let square = |x| x * x;
+        let a = (x).pipe(inc).pipe(double).pipe(square);
+        let b = square(double(inc(x)));
+        assert_eq!(a, b);
+    }
+
+    #[test]
+    fn type_transformation() {
+        let x = 'x';
+        let a = x.pipe(|x| (x, x, x)).pipe(|x| [x, x]);
+        let b = [('x', 'x', 'x'), ('x', 'x', 'x')];
+        assert_eq!(a, b);
+    }
+
+    #[test]
+    fn slice() {
+        let vec: &[i32] = &[0, 1, 2, 3];
+        let vec = vec.pipe(|x: &[i32]| [x, &[4, 5, 6]].concat());
+        assert_eq!(vec, [0, 1, 2, 3, 4, 5, 6]);
+    }
+
+    #[test]
+    fn trait_object() {
+        use core::{cmp::PartialEq, fmt::Display, marker::Copy};
+        fn run(x: impl AsRef<str> + PartialEq + Display + Copy + ?Sized) {
+            let x = x.pipe(|x| x);
+            assert_eq!(x.as_ref(), "abc");
+        }
+        run("abc");
+    }
+
+    #[test]
+    #[allow(clippy::blacklisted_name)]
+    fn pipe_ref() {
+        #[derive(Debug, PartialEq, Eq)]
+        struct FooBar(i32);
+        let foo = FooBar(12);
+        let bar = foo.pipe_ref(|x| x.0).pipe(FooBar);
+        assert_eq!(foo, bar);
+    }
+
+    #[test]
+    #[allow(clippy::blacklisted_name)]
+    fn pipe_ref_lifetime_bound() {
+        #[derive(Debug, PartialEq, Eq)]
+        struct Foo;
+        fn f(foo: &'_ Foo) -> &'_ Foo {
+            foo
+        }
+        Foo.pipe_ref(f).pipe_ref(f);
+    }
+
+    #[test]
+    #[allow(clippy::blacklisted_name)]
+    fn pipe_mut() {
+        #[derive(Debug, PartialEq, Eq)]
+        struct Foo(i32);
+        let mut foo = Foo(0);
+        foo.pipe_mut(|x| x.0 = 32);
+        assert_eq!(foo, Foo(32));
+    }
+
+    #[test]
+    #[allow(clippy::blacklisted_name)]
+    fn pipe_mut_lifetime_bound() {
+        #[derive(Debug, PartialEq, Eq)]
+        struct Foo(i32, i32, i32);
+        impl Foo {
+            pub fn new() -> Self {
+                Self(0, 0, 0)
+            }
+            pub fn set_0(&mut self, x: i32) -> &mut Self {
+                self.0 = x;
+                self
+            }
+            pub fn set_1(&mut self, x: i32) -> &mut Self {
+                self.1 = x;
+                self
+            }
+            pub fn set_2(&mut self, x: i32) -> &mut Self {
+                self.2 = x;
+                self
+            }
+        }
+
+        let mut expected = Foo::new();
+        let expected = expected.set_0(123).set_1(456).set_2(789);
+
+        fn modify(foo: &mut Foo) -> &mut Foo {
+            foo.set_0(123).set_1(456).set_2(789);
+            foo
+        }
+        let mut actual = Foo::new();
+        let actual = actual.pipe_mut(modify);
+
+        assert_eq!(actual, expected);
+    }
+}
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
new file mode 100644
index 0000000..619740f
--- /dev/null
+++ b/src/utils/mod.rs
@@ -0,0 +1,28 @@
+//! The `ruex` utilites.
+//!
+//! ```
+//! # #![allow(unused_imports)]
+//! use ruex::utils::*;
+//! ```
+
+pub mod terminal;
+
+pub mod panic;
+
+pub mod logging {
+    //! Configurable logging support.
+    //!
+    pub use fern::*;
+
+    pub use syslog;
+
+    pub use log::LevelFilter;
+}
+
+pub use humantime::*;
+
+pub mod build {
+    //! Build script and macro support.
+    //!
+    pub use companion::*;
+}
diff --git a/src/utils/panic/handler.rs b/src/utils/panic/handler.rs
new file mode 100644
index 0000000..67bc926
--- /dev/null
+++ b/src/utils/panic/handler.rs
@@ -0,0 +1,142 @@
+//! Here lives our custom panic handler and its support code
+//!
+//! ## Panic handler chaining
+//!
+//! Rust already has a pretty good panic handling mechanism that I see no need to reinvent.
+//! As a way to keep this functionality while still being able to print out a "Report this" message,
+//! we can simply chain the handlers together.
+//!
+//! The reason I am not making use of
+//! [`std::panic::update_hook`](https://doc.rust-lang.org/std/panic/fn.update_hook.html)
+//! is both because it is a nightly feature. Maybe this can be added in the future.
+
+use std::panic::PanicInfo;
+
+use url::Url;
+
+use crate::utils::terminal::{Colorize, Link, Stream};
+
+use super::validate::validate_repository;
+
+/// Contains metadata that we pull from the client crate with a macro
+#[derive(Debug)]
+pub struct PackageMetadata<'a> {
+    /// The client package name
+    pub pkg_name: &'a str,
+    /// The client crate version (bin or example)
+    pub crate_name: &'a str,
+    /// The client crate version
+    pub version: &'a str,
+    /// This may or may not be a repository URL.
+    /// Requires validation through `validate_repository()`
+    pub repository: &'a str,
+}
+
+/// Append a panic handler to the end of the chain of panic handlers
+///
+/// This function will execute whatever the existing panic handler is *before* executing the new one.
+/// Metadata is required to inform our crash reporter about the issue tracker URL.
+pub fn handler<F>(hook_fn: F, metadata: PackageMetadata<'static>)
+where
+    F: Fn(&PanicInfo<'_>, &PackageMetadata) + Sync + Send + 'static,
+{
+    // Get the current panic handler
+    let prev_hook = std::panic::take_hook();
+
+    // Create a new panic handler that chains our custom panic handler with the previous one
+    std::panic::set_hook(Box::new(move |panic_info| {
+        // Call the previous panic handler
+        prev_hook(panic_info);
+
+        // Call our custom panic handler
+        hook_fn(panic_info, &metadata);
+    }));
+}
+
+/// A panic handler that prints out a "Report this" message.
+///
+/// This will try to determine an issue tracker URL from the crate metadata and link it in the terminal with a pre-made message.
+pub fn suggest_issue_tracker(info: &PanicInfo<'_>, metadata: &PackageMetadata) {
+    // If color is disabled, we need to inform the `colored` crate before we start printing
+    if cfg!(not(feature = "color")) {
+        colored::control::set_override(false);
+    }
+
+    // We have access to the repo metadata.
+    // If we have not found a supported repository, we cannot proceed with linking an issue tracker.
+    // This requires validating the repository URL.
+    let mut report_url = None;
+
+    if !&metadata.repository.is_empty() {
+        if let Some(provider) = validate_repository(&Url::parse(metadata.repository).unwrap()) {
+            report_url = Some(provider.build_issue_url(info, metadata));
+        }
+    }
+
+    // Print the message
+    println!(
+        "{}",
+        format!(
+            "\n---------------------- {} ----------------------\n",
+            "Crash Detected".bold()
+        )
+        .red()
+    );
+
+    if let Some(url) = report_url {
+        // Print a message with a link to the issue tracker
+        println!(
+            "{}",
+            "This application has issue tracker support enabled.".italic()
+        );
+        println!(
+            "{}",
+            "Click the link below to report this crash to the developers.".bold()
+        );
+
+        // If this terminal supports clickable links, use one of those
+        // NOTE: VSCode both reports having clickable link support *and* does not let you click on the link
+        if crate::utils::terminal::on(Stream::Stdout)
+            && !std::env::var("TERM_PROGRAM")
+                .unwrap_or("unknown".to_string())
+                .contains("vscode")
+        {
+            let link = Link::new("Report Crash", url.as_str());
+            println!("\n[{}]", link.to_string().cyan().bold());
+        } else {
+            // Otherwise, just print the URL
+            println!("\n{}", url.to_string().cyan().bold());
+        }
+    } else {
+        // Well, this is awkward. Someone is using this crate without adding a repository key to their Cargo.toml.
+        if cfg!(debug_assertions) {
+            // If we are in a debug build. Warn the developer.
+            println!(
+                "{}",
+                "This application has issue tracker support enabled.".italic()
+            );
+            println!("However, it was not possible to determine the repository URL.");
+            println!(
+                "Please add a `{}` key to your {}.",
+                "repository".bright_green().bold(),
+                "Cargo.toml".cyan().bold()
+            );
+            println!(
+                "{}",
+                "\nThere is also a chance your repository service is not supported\nYou can request support at: https://github.com/ewpratten/crashreport-rs".italic()
+            );
+        } else {
+            // Just tell the user something went wrong
+            println!(
+                "{}",
+                "This application has issue tracker support enabled.".italic()
+            );
+            println!("However, it was not possible to determine the issue tracker URL.");
+        }
+    }
+
+    println!(
+        "{}",
+        "\n------------------------------------------------------------\n".red()
+    )
+}
diff --git a/src/utils/panic/mod.rs b/src/utils/panic/mod.rs
new file mode 100644
index 0000000..f08af1a
--- /dev/null
+++ b/src/utils/panic/mod.rs
@@ -0,0 +1,17 @@
+//! The panic handler better behaviour support.
+//!
+
+mod handler;
+pub use self::handler::*;
+
+mod provider;
+pub use self::provider::*;
+
+mod validate;
+pub use self::validate::*;
+
+pub mod backtrace {
+    //! The color backtrace support.
+    //!
+    pub use color_backtrace::*;
+}
diff --git a/src/utils/panic/provider.rs b/src/utils/panic/provider.rs
new file mode 100644
index 0000000..ebff6ab
--- /dev/null
+++ b/src/utils/panic/provider.rs
@@ -0,0 +1,63 @@
+use std::{panic::PanicInfo, time::SystemTime};
+
+use url::Url;
+
+use super::handler::PackageMetadata;
+
+/// Describes supported repository providers.
+#[derive(Debug)]
+pub enum RepositoryProvider {
+    /// Github provider
+    GitHub(Url),
+    /// Gitlab provider
+    GitLab(Url),
+}
+
+impl RepositoryProvider {
+    /// Returns the full URL required to file an issue in the repository of the client crate.
+    ///
+    /// Most Git providers allow issue creation through query params
+    pub fn build_issue_url(&self, info: &PanicInfo<'_>, metadata: &PackageMetadata) -> Url {
+        // No matter the provider, some of the strings are the same.
+        let body = vec![
+            "\n<h2>Information</h2>\n\n<!-- Please describe what happened to the best of your ability here -->",
+            "\n<h3>Error</h3>\n\n```",
+            &info.to_string(),
+            "```",
+            "\n<h3>Additional Info</h3>\n\n```",
+            &format!("OS: {}", std::env::consts::OS),
+            &format!("Architecture: {}", std::env::consts::ARCH),
+            &format!("Timestamp: {:?}", SystemTime::now()),
+            &format!("Version: {}", metadata.version),
+            &format!("Package: {}", metadata.pkg_name),
+            &format!("Crate: {}", metadata.crate_name),
+            "```\n",
+            "---",
+            "*This issue was auto-generated by the [`crashreport`](https://github.com/ewpratten/crashreport-rs) crate.*",
+            "*If you would like to improve these diagnostics, please contribute on GitHub.*"
+        ].join("\n");
+
+        match self {
+            RepositoryProvider::GitHub(url) => {
+                let mut output_url = url.clone();
+                let path = url.path();
+                output_url.set_path(&format!("{}/issues/new", path));
+                output_url.set_query(Some(&format!(
+                    "body={}&labels=bug",
+                    urlencoding::encode(&body)
+                )));
+                output_url
+            }
+            RepositoryProvider::GitLab(url) => {
+                let mut output_url = url.clone();
+                let path = url.path();
+                output_url.set_path(&format!("{}/issues/new", path));
+                output_url.set_query(Some(&format!(
+                    "issue[description]={}&issuable_template=bug",
+                    urlencoding::encode(&body)
+                )));
+                output_url
+            }
+        }
+    }
+}
diff --git a/src/utils/panic/validate.rs b/src/utils/panic/validate.rs
new file mode 100644
index 0000000..621523c
--- /dev/null
+++ b/src/utils/panic/validate.rs
@@ -0,0 +1,20 @@
+use url::Url;
+
+use super::provider::RepositoryProvider;
+
+/// Given a URL, tries to figure out the service provider of the repository.
+pub fn validate_repository(url: &Url) -> Option<RepositoryProvider> {
+    // Handle assumptions about the provider.
+    // NOTE: only one assume_* feature can be enabled at a time. Enabling more than one will cause the first to be used.
+    if cfg!(feature = "assume_github") {
+        return Some(RepositoryProvider::GitHub(url.clone()));
+    } else if cfg!(feature = "assume_gitlab") {
+        return Some(RepositoryProvider::GitLab(url.clone()));
+    }
+
+    match url.host_str() {
+        Some("github.com") => Some(RepositoryProvider::GitHub(url.clone())),
+        Some("gitlab.com") => Some(RepositoryProvider::GitLab(url.clone())),
+        _ => None,
+    }
+}
diff --git a/src/utils/terminal/mod.rs b/src/utils/terminal/mod.rs
new file mode 100644
index 0000000..dcbec0d
--- /dev/null
+++ b/src/utils/terminal/mod.rs
@@ -0,0 +1,100 @@
+//! The terminal color and links support.
+//!
+//!
+//! ```
+//! # #![allow(unused_imports)]
+//! use ruex::utils::terminal::*;
+//! ```
+
+use std::fmt;
+
+pub use colored::*;
+
+pub use atty::Stream;
+
+/// Returns true if the current terminal, detected through various environment
+/// variables, is known to support hyperlink rendering.
+pub fn supports_hyperlinks() -> bool {
+    // Hyperlinks can be forced through this env var.
+    if let Ok(arg) = std::env::var("FORCE_HYPERLINK") {
+        return arg.trim() != "0";
+    }
+
+    if std::env::var("DOMTERM").is_ok() {
+        // DomTerm
+        return true;
+    }
+
+    if let Ok(version) = std::env::var("VTE_VERSION") {
+        // VTE-based terminals above v0.50 (Gnome Terminal, Guake, ROXTerm, etc)
+        if version.parse().unwrap_or(0) >= 5000 {
+            return true;
+        }
+    }
+
+    if let Ok(program) = std::env::var("TERM_PROGRAM") {
+        if matches!(
+            &program[..],
+            "Hyper" | "iTerm.app" | "terminology" | "WezTerm"
+        ) {
+            return true;
+        }
+    }
+
+    if let Ok(term) = std::env::var("TERM") {
+        // Kitty
+        if matches!(&term[..], "xterm-kitty") {
+            return true;
+        }
+    }
+
+    // Windows Terminal and Konsole
+    std::env::var("WT_SESSION").is_ok() || std::env::var("KONSOLE_VERSION").is_ok()
+}
+
+/// Returns true if `stream` is a TTY, and the current terminal
+/// [supports_hyperlinks].
+pub fn on(stream: atty::Stream) -> bool {
+    (std::env::var("FORCE_HYPERLINK").is_ok() || atty::is(stream)) && supports_hyperlinks()
+}
+
+/// A clickable link component.
+#[derive(Clone, Debug, Eq, Hash, PartialEq)]
+pub struct Link<'a> {
+    /// link identifier
+    pub id: &'a str,
+    /// link text
+    pub text: &'a str,
+    /// link url
+    pub url: &'a str,
+}
+
+impl<'a> Link<'a> {
+    /// Create a new link with a name and target url.
+    pub fn new(text: &'a str, url: &'a str) -> Self {
+        Self { text, url, id: "" }
+    }
+
+    /// Create a new link with a name, a target url and an id.
+    pub fn with_id(text: &'a str, url: &'a str, id: &'a str) -> Self {
+        Self { text, url, id }
+    }
+}
+
+impl fmt::Display for Link<'_> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if self.id != "" {
+            write!(
+                f,
+                "\u{1b}]8;id={};{}\u{1b}\\{}\u{1b}]8;;\u{1b}\\",
+                self.id, self.url, self.text
+            )
+        } else {
+            write!(
+                f,
+                "\u{1b}]8;;{}\u{1b}\\{}\u{1b}]8;;\u{1b}\\",
+                self.url, self.text
+            )
+        }
+    }
+}

From 207aed400668b8e79d364e1f645249cf904ac258 Mon Sep 17 00:00:00 2001
From: Victor Dudochkin <dudochkin.victor@gmail.com>
Date: Sat, 25 Mar 2023 14:16:54 +0700
Subject: [PATCH 02/13] 0.1.4

---
 Cargo.toml                         |   2 +-
 macro/Cargo.toml                   |   1 +
 macro/src/impls/contract_aspect.rs |  69 +++++++++--------
 macro/src/impls/enums.rs           |  75 ++++++++++++++++++
 macro/src/impls/mod.rs             |   6 +-
 macro/src/impls/register.rs        | 120 +++++++++++++++++++----------
 macro/src/lib.rs                   |  29 ++++++-
 7 files changed, 227 insertions(+), 75 deletions(-)
 create mode 100644 macro/src/impls/enums.rs

diff --git a/Cargo.toml b/Cargo.toml
index a0e79c8..f3a71de 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ruex"
-version = "0.1.3"
+version = "0.1.4"
 authors = ["Victor Dudochkin <dudochkin.victor@gmail.com>"]
 readme = "README.md"
 homepage = "https://angular-rust.github.io/ruex"
diff --git a/macro/Cargo.toml b/macro/Cargo.toml
index eecbd91..39b6453 100644
--- a/macro/Cargo.toml
+++ b/macro/Cargo.toml
@@ -15,4 +15,5 @@ proc-macro-error = "1.0"
 serde_tokenstream = "0.1"
 syn-serde = { version = "0.2", features = ["json"] }
 serde = { version = "1", features = ["derive"] }
+serde_json = { version = "1.0" }
 rust-companion = "0"
\ No newline at end of file
diff --git a/macro/src/impls/contract_aspect.rs b/macro/src/impls/contract_aspect.rs
index 9186501..1430d9c 100644
--- a/macro/src/impls/contract_aspect.rs
+++ b/macro/src/impls/contract_aspect.rs
@@ -1,3 +1,4 @@
+#![allow(dead_code)]
 use std::{
     collections::{HashMap, VecDeque},
     fmt,
@@ -751,7 +752,12 @@ impl SyntaxAndDocs for Vec<Contract> {
 }
 
 // process macro attribute by initiating currentt case and handle rest of cases
-pub(crate) fn contracts_aspect(ty: Type, mode: Mode, attrs: TokenStream2, item: TokenStream2) -> TokenStream2 {
+pub(crate) fn contracts_aspect(
+    ty: Type,
+    mode: Mode,
+    attrs: TokenStream2,
+    item: TokenStream2,
+) -> TokenStream2 {
     let mut state: ContractAspectState = ContractAspectState::default();
 
     // process primary contract case
@@ -807,14 +813,16 @@ pub(crate) fn contracts_aspect(ty: Type, mode: Mode, attrs: TokenStream2, item:
         .aspects
         .iter()
         .filter_map(|aspect| {
-            if let Some(item) = &aspect.before && let Some(block) = &item.default {
-            let stmts = &block.stmts;
-            Some(quote!{
-                #(#stmts)*
-            })
-        } else {
-            None
-        }
+            if let Some(item) = &aspect.before {
+                item.default.as_ref().map(|block| {
+                    let stmts = &block.stmts;
+                    quote! {
+                        #(#stmts)*
+                    }
+                })
+            } else {
+                None
+            }
         })
         .collect::<Vec<_>>();
 
@@ -823,10 +831,12 @@ pub(crate) fn contracts_aspect(ty: Type, mode: Mode, attrs: TokenStream2, item:
         .iter()
         .rev()
         .filter_map(|aspect| {
-            if let Some(item) = &aspect.after && let Some(block) = &item.default {
-                let stmts = &block.stmts;
-                Some(quote!{
-                    #(#stmts)*
+            if let Some(item) = &aspect.after {
+                item.default.as_ref().map(|block| {
+                    let stmts = &block.stmts;
+                    quote! {
+                        #(#stmts)*
+                    }
                 })
             } else {
                 None
@@ -841,24 +851,23 @@ pub(crate) fn contracts_aspect(ty: Type, mode: Mode, attrs: TokenStream2, item:
 
     let mut it = state.aspects.iter().rev().peekable();
     while let Some(aspect) = it.next() {
-        if let Some(item) = &aspect.around && let Some(block) = &item.default {
+        if let Some(item) = &aspect.around {
+            item.default.as_ref().map(|block| {
+                let mut replacer = AspectJointPoint { stream: &around };
 
-            let mut replacer = AspectJointPoint {
-                stream: &around,
-            };
-            
-            let mut stmts = block.stmts.clone();
+                let mut stmts = block.stmts.clone();
 
-            for stmt in stmts.iter_mut() {
-                replacer.visit_stmt_mut(stmt);
-            }
-            
-            if it.peek().is_none() {
-                around = quote!(#(#stmts)*);
-            } else {
-                around = quote!({#(#stmts)*});
-            }
-            has_around = true;
+                for stmt in stmts.iter_mut() {
+                    replacer.visit_stmt_mut(stmt);
+                }
+
+                if it.peek().is_none() {
+                    around = quote!(#(#stmts)*);
+                } else {
+                    around = quote!({#(#stmts)*});
+                }
+                has_around = true;
+            });
         }
     }
 
@@ -955,4 +964,4 @@ pub(crate) fn contracts_aspect(ty: Type, mode: Mode, attrs: TokenStream2, item:
             result
         }
     }
-}
\ No newline at end of file
+}
diff --git a/macro/src/impls/enums.rs b/macro/src/impls/enums.rs
new file mode 100644
index 0000000..023ecc7
--- /dev/null
+++ b/macro/src/impls/enums.rs
@@ -0,0 +1,75 @@
+#![allow(dead_code)]
+#![allow(unused_imports)]
+#![allow(unused_variables)]
+// necessary for the TokenStream::from_str() implementation
+use std::{collections::HashMap, net::UdpSocket, path::PathBuf, str::FromStr};
+
+use companion::{companion_addr, Response, Task};
+// use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::{format_ident, quote};
+use serde::{Deserialize, Serialize, Serializer};
+use serde_tokenstream::from_tokenstream;
+use syn::{Item, ItemFn, ItemStruct, Path};
+
+use crate::MAX_UDP_PAYLOAD;
+
+#[derive(Default, Debug, Clone, Serialize, Deserialize)]
+pub(crate) struct ItemLocation {
+    pub path: String,
+    pub range: (usize, usize),
+}
+
+#[derive(Default, Debug, Clone, Serialize, Deserialize)]
+pub(crate) struct Enum {
+    pub supertraits: Vec<String>,
+    pub items: HashMap<String, ItemLocation>,
+}
+
+pub(crate) fn mount(
+    attrs: TokenStream2,
+    input: TokenStream2,
+    location: ItemLocation,
+) -> TokenStream2 {
+    let path: Path = syn::parse2(attrs).unwrap();
+    let path = path
+        .segments
+        .iter()
+        .map(|item| item.ident.to_string())
+        .collect::<Vec<String>>()
+        .join("::");
+    let item: Item = syn::parse2(input.clone()).unwrap();
+    match item {
+        Item::Struct(item) => {
+            let ident = item.ident.to_string();
+            // println!("Place {ident} into {path}");
+            let mut buf = [0; MAX_UDP_PAYLOAD];
+
+            let addr = companion_addr();
+
+            let socket = UdpSocket::bind("[::]:0").unwrap();
+            socket.connect(addr).unwrap();
+
+            socket.send(&Task::Get(&path).as_bytes()).unwrap();
+            let (len, _src) = socket.recv_from(&mut buf).unwrap();
+            let resp = Response::from(&buf[..len]);
+
+            if let Response::String(data) = resp {
+                // println!("{data}");
+                let mut data: Enum = serde_json::from_str(&data).unwrap();
+                data.items.insert(ident, location);
+                // println!("{data:#?}");
+
+                let data = serde_json::to_string(&data).unwrap();
+                // println!("{data}");
+                socket.send(&Task::Set(&path, &data).as_bytes()).unwrap();
+                let (len, _src) = socket.recv_from(&mut buf).unwrap();
+                let _resp = Response::from(&buf[..len]);
+            } else {
+                panic!("Enum Trait `{}` is not registered", path)
+            }
+        }
+        _ => panic!("Struct only supported for mounting"),
+    }
+    input
+}
diff --git a/macro/src/impls/mod.rs b/macro/src/impls/mod.rs
index f83d930..60bdb0f 100644
--- a/macro/src/impls/mod.rs
+++ b/macro/src/impls/mod.rs
@@ -1,4 +1,3 @@
-#![allow(dead_code)]
 #[allow(unused_imports)]
 use proc_macro::TokenStream;
 use serde::Deserialize;
@@ -15,6 +14,9 @@ pub(crate) use self::decorate::*;
 mod delegate;
 pub(crate) use self::delegate::*;
 
+mod enums;
+pub(crate) use self::enums::*;
+
 mod mock;
 pub(crate) use self::mock::*;
 
@@ -71,7 +73,7 @@ enum BactraceLevel {
     Full,
 }
 
-// #[cfg(not(test))] // Work around for rust-lang/rust#62127
+#[cfg(not(test))] // Work around for rust-lang/rust#62127
 pub(crate) fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
     let attr = proc_macro2::TokenStream::from(attr);
     let config = if !attr.is_empty() {
diff --git a/macro/src/impls/register.rs b/macro/src/impls/register.rs
index 278d32c..bbe39dd 100644
--- a/macro/src/impls/register.rs
+++ b/macro/src/impls/register.rs
@@ -1,21 +1,19 @@
 use std::{
-    collections::VecDeque,
+    collections::{HashMap, VecDeque},
     net::UdpSocket,
 };
 
-use companion::{companion_addr, Task, Response};
+use companion::{companion_addr, Response, Task};
 use proc_macro2::{Span, TokenStream as TokenStream2};
 use quote::{quote, ToTokens};
 use syn::{
-    visit_mut::VisitMut,
-    Attribute, FnArg, Ident, Item,
-     ItemTrait, Pat, Path, 
-    TraitItem, TraitItemMethod,
+    visit_mut::VisitMut, Attribute, FnArg, Ident, Item, ItemTrait, Pat, Path, TraitItem,
+    TraitItemMethod,
 };
 
 use crate::MAX_UDP_PAYLOAD;
 
-use super::{AspectJointPoint, Type, ContractAspectState, SyntaxAndDocs};
+use super::{AspectJointPoint, ContractAspectState, Enum, SyntaxAndDocs, Type};
 
 /// Name used for the "re-routed" method.
 fn trait_method_impl_name(name: &str) -> String {
@@ -40,9 +38,7 @@ fn method_rename(method: &TraitItemMethod) -> TraitItemMethod {
             .filter(|attr| {
                 let name = attr.path.segments.last().unwrap().ident.to_string();
 
-                Type::type_and_mode(&name).is_none()
-                    && name != "aspect"
-                    && name != "doc"
+                Type::type_and_mode(&name).is_none() && name != "aspect" && name != "doc"
             })
             .cloned(),
     );
@@ -184,10 +180,12 @@ fn process_item_trait(path: String, mut input: ItemTrait) -> TokenStream2 {
                         .aspects
                         .iter()
                         .filter_map(|aspect| {
-                            if let Some(item) = &aspect.before && let Some(block) = &item.default {
-                                let stmts = &block.stmts;
-                                Some(quote!{
-                                    #(#stmts)*
+                            if let Some(item) = &aspect.before {
+                                item.default.as_ref().map(|block| {
+                                    let stmts = &block.stmts;
+                                    quote! {
+                                        #(#stmts)*
+                                    }
                                 })
                             } else {
                                 None
@@ -200,14 +198,16 @@ fn process_item_trait(path: String, mut input: ItemTrait) -> TokenStream2 {
                         .iter()
                         .rev()
                         .filter_map(|aspect| {
-                            if let Some(item) = &aspect.after && let Some(block) = &item.default {
+                            if let Some(item) = &aspect.after {
+                                item.default.as_ref().map(|block| {
                                     let stmts = &block.stmts;
-                                    Some(quote!{
+                                    quote! {
                                         #(#stmts)*
-                                    })
-                                } else {
-                                    None
-                                }
+                                    }
+                                })
+                            } else {
+                                None
+                            }
                         })
                         .collect::<Vec<_>>();
 
@@ -220,24 +220,23 @@ fn process_item_trait(path: String, mut input: ItemTrait) -> TokenStream2 {
 
                     let mut it = state.aspects.iter().rev().peekable();
                     while let Some(aspect) = it.next() {
-                        if let Some(item) = &aspect.around && let Some(block) = &item.default {
-                            
-                            let mut replacer = AspectJointPoint {
-                                stream: &around,
-                            };
-
-                            let mut stmts = block.stmts.clone();
-                            
-                            for stmt in stmts.iter_mut() {
-                                replacer.visit_stmt_mut(stmt);
-                            }
-                            
-                            if it.peek().is_none() {
-                                around = quote!(#(#stmts)*);
-                            } else {
-                                around = quote!({#(#stmts)*});
-                            }
-                            has_around = true;
+                        if let Some(item) = &aspect.around {
+                            item.default.as_ref().map(|block| {
+                                let mut replacer = AspectJointPoint { stream: &around };
+
+                                let mut stmts = block.stmts.clone();
+
+                                for stmt in stmts.iter_mut() {
+                                    replacer.visit_stmt_mut(stmt);
+                                }
+
+                                if it.peek().is_none() {
+                                    around = quote!(#(#stmts)*);
+                                } else {
+                                    around = quote!({#(#stmts)*});
+                                }
+                                has_around = true;
+                            });
                         }
                     }
 
@@ -400,6 +399,47 @@ fn process_remote_trait(path: String, mut input: ItemTrait) -> TokenStream2 {
     TokenStream2::new()
 }
 
+fn process_enum_trait(path: String, input: ItemTrait) -> TokenStream2 {
+    let supertraits = input
+        .supertraits
+        .iter()
+        .filter_map(|item| match item {
+            syn::TypeParamBound::Trait(item) => {
+                //
+                Some(
+                    item.path
+                        .segments
+                        .iter()
+                        .map(|segment| segment.ident.to_string())
+                        .collect::<Vec<_>>()
+                        .join("::"),
+                )
+            }
+            syn::TypeParamBound::Lifetime(_) => None,
+        })
+        .collect::<Vec<_>>();
+
+    let item = Enum {
+        supertraits,
+        items: HashMap::new(),
+    };
+
+    let data = serde_json::to_string(&item).unwrap();
+
+    let mut buf = [0; MAX_UDP_PAYLOAD];
+
+    let addr = companion_addr();
+
+    let socket = UdpSocket::bind("[::]:0").unwrap();
+    socket.connect(addr).unwrap();
+
+    socket.send(&Task::Set(&path, &data).as_bytes()).unwrap();
+    let (len, _src) = socket.recv_from(&mut buf).unwrap();
+    let _resp = Response::from(&buf[..len]);
+
+    TokenStream2::new()
+}
+
 fn process_aspect_trait(path: String, input: ItemTrait) -> TokenStream2 {
     input.items.iter().for_each(|item| {
         // check input
@@ -450,10 +490,12 @@ pub(crate) fn register(attrs: TokenStream2, input: TokenStream2) -> TokenStream2
                 process_aspect_trait(path, item)
             } else if ident.ends_with("Remote") {
                 process_remote_trait(path, item)
+            } else if ident.ends_with("Enum") {
+                process_enum_trait(path, item)
             } else {
                 process_item_trait(path, item)
             }
         }
         _ => panic!("Traits only supported for registration"),
     }
-}
\ No newline at end of file
+}
diff --git a/macro/src/lib.rs b/macro/src/lib.rs
index 8dfbe17..9e6c32a 100644
--- a/macro/src/lib.rs
+++ b/macro/src/lib.rs
@@ -1,3 +1,5 @@
+#![feature(proc_macro_span)]
+#![allow(unused_imports)]
 #![allow(clippy::needless_doctest_main)]
 // #![warn(
 //     missing_debug_implementations,
@@ -10,10 +12,10 @@
     attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
 ))]
 
-use proc_macro::TokenStream;
+use proc_macro::{Span, TokenStream, TokenTree};
 
 mod impls;
-use impls::{Mode, Type};
+use impls::{ItemLocation, Mode, Type};
 
 const MAX_UDP_PAYLOAD: usize = 65507;
 
@@ -81,7 +83,7 @@ const MAX_UDP_PAYLOAD: usize = 65507;
 /// }
 /// ```
 #[proc_macro_attribute]
-// #[cfg(not(test))] // Work around for rust-lang/rust#62127
+#[cfg(not(test))] // Work around for rust-lang/rust#62127
 pub fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
     impls::main(attr, item)
 }
@@ -328,6 +330,27 @@ pub fn register(attr: TokenStream, item: TokenStream) -> TokenStream {
     impls::register(attr.into(), item.into()).into()
 }
 
+/// Subject mounting.
+#[proc_macro_attribute]
+pub fn mount(attr: TokenStream, item: TokenStream) -> TokenStream {
+    let mut it = item.clone().into_iter();
+    let first = it.next().unwrap();
+    let last = it.last().unwrap();
+
+    let pos = ItemLocation {
+        path: first
+            .span()
+            .source_file()
+            .path()
+            .to_str()
+            .unwrap()
+            .to_string(),
+        range: (first.span().start().line, last.span().end().line),
+    };
+
+    impls::mount(attr.into(), item.into(), pos).into()
+}
+
 /// Aspect-oriented methodology.
 ///
 #[proc_macro_attribute]

From 8b43c86b7a885a54bb3e63bb105230f4e586a9a1 Mon Sep 17 00:00:00 2001
From: Victor Dudochkin <dudochkin.victor@gmail.com>
Date: Sat, 25 Mar 2023 14:24:32 +0700
Subject: [PATCH 03/13] update

---
 Cargo.toml       | 2 +-
 macro/Cargo.toml | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index f3a71de..d36ea2d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ruex"
-version = "0.1.4"
+version = "0.1.5"
 authors = ["Victor Dudochkin <dudochkin.victor@gmail.com>"]
 readme = "README.md"
 homepage = "https://angular-rust.github.io/ruex"
diff --git a/macro/Cargo.toml b/macro/Cargo.toml
index 39b6453..325d92c 100644
--- a/macro/Cargo.toml
+++ b/macro/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ruex-macro"
-version = "0.1.0"
+version = "0.1.1"
 authors = ["Victor Dudochkin <dudochkin.victor@gmail.com>"]
 edition = "2018"
 

From 71f485a3d4cf8174bb921cdf1066be7c3cebecb1 Mon Sep 17 00:00:00 2001
From: Victor Dudochkin <dudochkin.victor@gmail.com>
Date: Sat, 25 Mar 2023 14:36:16 +0700
Subject: [PATCH 04/13] fix for 0.1.4

---
 macro/Cargo.toml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/macro/Cargo.toml b/macro/Cargo.toml
index 325d92c..69d72b1 100644
--- a/macro/Cargo.toml
+++ b/macro/Cargo.toml
@@ -1,8 +1,10 @@
 [package]
 name = "ruex-macro"
+description = "Proc-macro for RUEX"
 version = "0.1.1"
 authors = ["Victor Dudochkin <dudochkin.victor@gmail.com>"]
 edition = "2018"
+license = "MPL-2.0"
 
 [lib]
 proc-macro = true

From 740b8d98d3c2d5765106d64df27e9ab35c3a91d6 Mon Sep 17 00:00:00 2001
From: Victor Dudochkin <dudochkin.victor@gmail.com>
Date: Sun, 26 Mar 2023 02:59:26 +0700
Subject: [PATCH 05/13] fixes

---
 macro/Cargo.toml |  2 +-
 macro/src/lib.rs | 27 +++++++++++++++------------
 2 files changed, 16 insertions(+), 13 deletions(-)

diff --git a/macro/Cargo.toml b/macro/Cargo.toml
index 69d72b1..6a4b100 100644
--- a/macro/Cargo.toml
+++ b/macro/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "ruex-macro"
 description = "Proc-macro for RUEX"
-version = "0.1.1"
+version = "0.1.2"
 authors = ["Victor Dudochkin <dudochkin.victor@gmail.com>"]
 edition = "2018"
 license = "MPL-2.0"
diff --git a/macro/src/lib.rs b/macro/src/lib.rs
index 9e6c32a..bedc21f 100644
--- a/macro/src/lib.rs
+++ b/macro/src/lib.rs
@@ -1,4 +1,4 @@
-#![feature(proc_macro_span)]
+// #![feature(proc_macro_span)]
 #![allow(unused_imports)]
 #![allow(clippy::needless_doctest_main)]
 // #![warn(
@@ -333,19 +333,22 @@ pub fn register(attr: TokenStream, item: TokenStream) -> TokenStream {
 /// Subject mounting.
 #[proc_macro_attribute]
 pub fn mount(attr: TokenStream, item: TokenStream) -> TokenStream {
-    let mut it = item.clone().into_iter();
-    let first = it.next().unwrap();
-    let last = it.last().unwrap();
+    // let mut it = item.clone().into_iter();
+    // let first = it.next().unwrap();
+    // let last = it.last().unwrap();
 
+    // let path = first
+    //         .span()
+    //         .source_file()
+    //         .path()
+    //         .to_str()
+    //         .unwrap()
+    //         .to_string();
+    // let range = (first.span().start().line, last.span().end().line);
+    let path = String::new();
     let pos = ItemLocation {
-        path: first
-            .span()
-            .source_file()
-            .path()
-            .to_str()
-            .unwrap()
-            .to_string(),
-        range: (first.span().start().line, last.span().end().line),
+        path,
+        range: (0, 0),
     };
 
     impls::mount(attr.into(), item.into(), pos).into()

From a879771130162cb2350b7d15eeb4b378478d09a1 Mon Sep 17 00:00:00 2001
From: Victor Dudochkin <dudochkin.victor@gmail.com>
Date: Sun, 26 Mar 2023 03:07:09 +0700
Subject: [PATCH 06/13] wtf

---
 src/prelude/compose.rs | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/src/prelude/compose.rs b/src/prelude/compose.rs
index d3a3178..08f2640 100644
--- a/src/prelude/compose.rs
+++ b/src/prelude/compose.rs
@@ -2,13 +2,6 @@
 ///
 /// Allows to compose functions and closures.
 ///
-/// ```no_run
-/// let chained = checkit
-///   .chain(add_data)
-///   .chain(add_experiments)
-///   .chain(add_data)
-///   .chain(|inout| format!("{input}/README.md"));
-/// ```
 pub trait Compose<In, Out>: Fn(In) -> Out {
     /// `impl Trait` only allowed in function and inherent method return types,
     /// not in trait method return - so its boxed

From d4dfd9e226ff5ea6c20050351c49f90901320ac2 Mon Sep 17 00:00:00 2001
From: Victor Dudochkin <dudochkin.victor@gmail.com>
Date: Sun, 26 Mar 2023 03:10:43 +0700
Subject: [PATCH 07/13] 013

---
 macro/Cargo.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/macro/Cargo.toml b/macro/Cargo.toml
index 6a4b100..55e448b 100644
--- a/macro/Cargo.toml
+++ b/macro/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "ruex-macro"
 description = "Proc-macro for RUEX"
-version = "0.1.2"
+version = "0.1.3"
 authors = ["Victor Dudochkin <dudochkin.victor@gmail.com>"]
 edition = "2018"
 license = "MPL-2.0"

From 4425e5ee6b4805fda252f87b332978a4a93c91cb Mon Sep 17 00:00:00 2001
From: Victor Dudochkin <dudochkin.victor@gmail.com>
Date: Sun, 26 Mar 2023 03:17:31 +0700
Subject: [PATCH 08/13] fix

---
 src/utils/terminal/mod.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/utils/terminal/mod.rs b/src/utils/terminal/mod.rs
index dcbec0d..eafe833 100644
--- a/src/utils/terminal/mod.rs
+++ b/src/utils/terminal/mod.rs
@@ -83,7 +83,7 @@ impl<'a> Link<'a> {
 
 impl fmt::Display for Link<'_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if self.id != "" {
+        if !self.id.is_empty() {
             write!(
                 f,
                 "\u{1b}]8;id={};{}\u{1b}\\{}\u{1b}]8;;\u{1b}\\",

From 528eed273d01e5bce7eed74993c2768d34635720 Mon Sep 17 00:00:00 2001
From: Victor Dudochkin <dudochkin.victor@gmail.com>
Date: Sun, 26 Mar 2023 03:22:51 +0700
Subject: [PATCH 09/13] curry

---
 src/prelude/currying.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/prelude/currying.rs b/src/prelude/currying.rs
index 87320b9..171eb11 100644
--- a/src/prelude/currying.rs
+++ b/src/prelude/currying.rs
@@ -4,7 +4,7 @@
 
 /// Currying for functions with 2 params
 ///
-/// ```
+/// ```no_run
 /// fn two_params(a1: &str, a2: &str) -> String {
 ///     format!("{a1}/{a2}")
 /// }

From 88d0105c852057b0893eff6b8760dfef309499f7 Mon Sep 17 00:00:00 2001
From: Victor Dudochkin <dudochkin.victor@gmail.com>
Date: Sun, 26 Mar 2023 03:31:29 +0700
Subject: [PATCH 10/13] doc fix

---
 src/prelude/currying.rs | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/src/prelude/currying.rs b/src/prelude/currying.rs
index 171eb11..08b0cfa 100644
--- a/src/prelude/currying.rs
+++ b/src/prelude/currying.rs
@@ -4,14 +4,6 @@
 
 /// Currying for functions with 2 params
 ///
-/// ```no_run
-/// fn two_params(a1: &str, a2: &str) -> String {
-///     format!("{a1}/{a2}")
-/// }
-///
-/// let one = two_params.curry("/mnt");
-/// println!("{}", one("home"));
-/// ```
 pub trait Curry<A, B> {
     /// The concrete type that `curry` returns.
     type Output;

From 925ed7f5d04f435801528f19f2861a24f8d86737 Mon Sep 17 00:00:00 2001
From: Victor Dudochkin <dudochkin.victor@gmail.com>
Date: Wed, 29 Mar 2023 11:42:19 +0700
Subject: [PATCH 11/13] check actions

---
 Cargo.toml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Cargo.toml b/Cargo.toml
index d36ea2d..af7fb7e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,6 +32,8 @@ syslog = "6"
 # contracts = "0.6"
 # systemd-journal-logger = "0.7"
 rust-companion = "0"
+quanta = "0.11.0"
+perf-event = "0.4.8"
 
 [build-dependencies]
-rust-companion = "0"
\ No newline at end of file
+rust-companion = "0"

From a5e93ce8cc1a051a8a6c8364545605c66c6a31ae Mon Sep 17 00:00:00 2001
From: Victor Dudochkin <dudochkin.victor@gmail.com>
Date: Wed, 29 Mar 2023 11:49:36 +0700
Subject: [PATCH 12/13] coverage

---
 .github/workflows/coverage.yaml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml
index b9cf34a..40a0007 100644
--- a/.github/workflows/coverage.yaml
+++ b/.github/workflows/coverage.yaml
@@ -34,6 +34,7 @@ jobs:
         uses: actions-rs/tarpaulin@v0.1
         with:
           args: '--all-features --run-types Doctests,Tests'
+          version: '0.15.0'
           timeout: 120
 
       - name: Upload to codecov.io

From 6d0f42ef72fd8246ae11b216f497f1fb90a47d5d Mon Sep 17 00:00:00 2001
From: Victor Dudochkin <dudochkin.victor@gmail.com>
Date: Wed, 29 Mar 2023 12:09:28 +0700
Subject: [PATCH 13/13] badge fix

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 1756acb..af5d82b 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@
 [license-url]: https://github.com/angular-rust/ruex/blob/master/LICENSE
 [gitter-badge]: https://img.shields.io/gitter/room/angular_rust/community.svg?style=flat-square
 [gitter-url]: https://gitter.im/angular_rust/community
-[tests-badge]: https://img.shields.io/github/workflow/status/angular-rust/ruex/Tests?label=tests&logo=github&style=flat-square
+[tests-badge]: https://img.shields.io/github/actions/workflow/status/angular-rust/ruex/tests.yml?label=tests&logo=github&style=flat-square
 [tests-url]: https://github.com/angular-rust/ruex/actions/workflows/tests.yml
 [codecov-badge]: https://img.shields.io/codecov/c/github/angular-rust/ruex?logo=codecov&style=flat-square&token=L7KV27OLY0
 [codecov-url]: https://codecov.io/gh/angular-rust/ruex
