ordes-macros 0.2.1

Utility proc macros used in ordes to generate OrdesDec and OrdesInc implementations.
Documentation
use proc_macro::TokenStream;
use syn::{LitInt, parse::{Parse, ParseStream, Result as SynResult}, parse_macro_input};

struct CollectionLen {
    len: usize,
}

impl Parse for CollectionLen {
    fn parse(input: ParseStream) -> SynResult<CollectionLen> {
        input.parse::<LitInt>().map(|litint| litint.base10_parse::<usize>())?.map(|len| CollectionLen { len })
    }
}

#[proc_macro]
pub fn impl_ops_arr(input: TokenStream) -> TokenStream {
    let CollectionLen { len } = parse_macro_input!(input as CollectionLen);
    let self_destructure = (0..len).map(|e| format!("e_{}", e)).collect::<Vec<_>>();
    let mut expanded = String::new();
    if len > 1 {
        let decimpl = format!(
            "impl<T> OrdesPop for [T; {len}] {{\n    \
                type Newlen = [T; {len_less_1}];\n    \
                type Output = T;\n\n    \
                fn pop(self) -> (Self::Newlen, Self::Output) {{\n        \
                    let [{self_destructure}] = self;\n        \
                    ([{self_destructure_less_end}], {last})\n    \
                }}\n\
            }}\n\n
            impl<T> OrdesRest for [T; {len}] {{\n    \
                type Newlen = [T; {len_less_1}];\n    \
                type Output = T;\n\n    \
                fn rest(self) -> (Self::Newlen, Self::Output) {{\n        
                    let [{self_destructure}] = self;\n        \
                    ([{self_destructure_less_start}], {first})\n    \
                }}\n\
            }}\n",
            len = len,
            len_less_1 = len - 1,
            self_destructure = self_destructure.join(", "),
            self_destructure_less_end = self_destructure[..len - 1].join(", "),
            last = self_destructure[len - 1],
            self_destructure_less_start = self_destructure[1..].join(","),
            first = self_destructure[0]
        );
        expanded.push_str(&decimpl);
    } else if len == 1 {
        let decimpl = "\
            impl<T> OrdesPop for [T; 1] {\n    \
                type Newlen = ();\n    \
                type Output = T;\n\n    \
                fn pop(self) -> (Self::Newlen, T) {\n        \
                    let [e_0] = self;\n        \
                    ((), e_0)\n    \
                }\n\
            }\n\n\
            impl<T> OrdesRest for [T; 1] {\n    \
                type Newlen = ();\n    \
                type Output = T;\n\n    \
                fn rest(self) -> (Self::Newlen, T) {\n        \
                    let [e_0] = self;
                    ((), e_0)\n    \
                }\n\
            }\n\
        ";
        expanded.push_str(decimpl);
    }
    if len < std::usize::MAX {
        let incimpl = format!(
            "impl<T> OrdesPush<T> for [T; {len}] {{\n    \
                type Output = [T; {len_plus_1}];\n\n    \
                fn push(self, value: T) -> Self::Output {{\n        \
                    let [{self_destructure}] = self;\n        \
                    [{self_destructure}, value]\n    \
                }}\n\
            }}\n\n\
            impl<T> OrdesCons<T> for [T; {len}] {{\n    \
                type Output = [T; {len_plus_1}];\n\n    \
                fn cons(self, value: T) -> Self::Output {{\n        \
                    let [{self_destructure}] = self;\n        \
                    [value, {self_destructure}]\n    \
                }}\n\
            }}\n",
            len = len,
            len_plus_1 = len + 1,
            self_destructure = self_destructure.join(", "),
        );
        expanded.push_str(&incimpl);
    }
    expanded.parse().unwrap()
}

#[proc_macro]
pub fn impl_ops_tuple(input: TokenStream) -> TokenStream {
    let CollectionLen { len } = parse_macro_input!(input as CollectionLen);
    let self_type_params = (0..len).map(|t| format!("T{}", t)).collect::<Vec<_>>();
    let self_destructure = (0..len).map(|e| format!("e_{}", e)).collect::<Vec<_>>();
    let mut expanded = String::new();
    if len > 1 {
        let decimpl = format!(
            "impl<{self_types}> OrdesPop for ({self_types}) {{\n    \
                type Newlen = ({self_types_less_last});\n    \
                type Output = {last_type};\n\n    \
                fn pop(self) -> (Self::Newlen, {last_type}) {{\n        \
                    let ({self_destructure}) = self;\n        \
                    (({self_destructure_less_last}), {last})\n    \
                }}\n\
            }}
            impl<{self_types}> OrdesRest for ({self_types}) {{\n    \
                type Newlen = ({self_types_less_first});\n    \
                type Output = {first_type};\n\n    \
                fn rest(self) -> (Self::Newlen, {first_type}) {{\n        \
                    let ({self_destructure}) = self;\n        \
                    (({self_destructure_less_first}), {first})
                }}\n\
            }}\n",
            self_types = self_type_params.join(", "),
            self_types_less_last = self_type_params[..len - 1].join(", "),
            last_type = self_type_params[len - 1],
            self_destructure = self_destructure.join(", "),
            self_destructure_less_last = self_destructure[..len - 1].join(", "),
            last = self_destructure[len - 1],
            self_types_less_first = self_type_params[1..].join(", "),
            first_type = self_type_params[0],
            self_destructure_less_first = self_destructure[1..].join(", "),
            first = self_destructure[0]
        );
        expanded.push_str(&decimpl);
    } else if len == 1 {
        let decimpl = "\
            impl<T0> OrdesPop for (T0,) {\n    \
                type Newlen = ();\n    \
                type Output = T0;\n\n    \
                fn pop(self) -> (Self::Newlen, T0) {\n        \
                    let (e_0,) = self;\n        \
                    ((), e_0)\n    \
                }\n\
            }\n\n\
            impl<T0> OrdesRest for (T0,) {\n    \
                type Newlen = ();\n    \
                type Output = T0;\n\n    \
                fn rest(self) -> (Self::Newlen, T0) {\n        \
                    let (e_0,) = self;\n        \
                    ((), e_0)\n    \
                }\n\
            }\n\
        ";
        expanded.push_str(decimpl);
    }
    if len < std::usize::MAX && len > 1 {
        let incimpl = format!(
            "impl<{self_types}, {extra_type}> OrdesPush<{extra_type}> for ({self_types}) {{\n    \
                type Output = ({self_types}, {extra_type});\n\n    \
                fn push(self, value: {extra_type}) -> Self::Output {{\n        \
                    let ({self_destructure}) = self;\n        \
                    ({self_destructure}, value)\n    \
                }}\n\
            }}\n\n\
            impl<{self_types}, {extra_type}> OrdesCons<{extra_type}> for ({self_types}) {{\n    \
                type Output = ({extra_type}, {self_types});\n\n    \
                fn cons(self, value: {extra_type}) -> Self::Output {{\n        \
                    let ({self_destructure}) = self;\n        \
                    (value, {self_destructure})\n    \
                }}\n\
            }}\n",
            self_types = self_type_params.join(", "),
            extra_type = format!("T{}", len),
            self_destructure = self_destructure.join(", "),
        );
        expanded.push_str(&incimpl);
    } else if len == 1 {
        let incimpl = "\
            impl<T0, T1> OrdesPush<T1> for (T0,) {\n    \
                type Output = (T0, T1);\n\n    \
                fn push(self, value: T1) -> Self::Output {\n        \
                    let (e_0,) = self;\n        \
                    (e_0, value)\n    \
                }\n\
            }\n\n\
            impl<T0, T1> OrdesCons<T1> for (T0,) {\n    \
                type Output = (T1, T0);\n\n    \
                fn cons(self, value: T1) -> Self::Output {\n        \
                    let (e_0,) = self;\n        \
                    (value, e_0)\n    \
                }\n\
            }\
        ";
        expanded.push_str(incimpl);
    }
    expanded.parse().unwrap()
}