1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
macro_rules! message_display {
    ($enumname:ident) => {
        impl fmt::Display for $enumname {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "{}", self.get_message().unwrap())
            }
        }
    }
}

macro_rules! inputs {
    () => {};

    ($(#[$attr:meta])* fn $fnname:ident -> $fnret:ty, $varname:expr, |($kp:tt, $vp:tt)| $code:expr; $($rest:tt)*) => {
        $(#[$attr])*
        pub fn $fnname() -> $crate::Result<$fnret> {
            ::std::env::vars_os()
                .filter_map(|(k, $vp)| match k.into_string() {
                    Ok(ref mut s) if s.starts_with($varname) => {
                        let $kp = &s[$varname.len()..];
                        $code
                    }

                    _ => None
                })
                .collect()
        }

        inputs!($($rest)*);
    };

    ($(#[$attr:meta])* fn $fnname:ident -> $fnret:ty, $varname:expr, |$param:tt| $code:expr; $($rest:tt)*) => {
        $(#[$attr])*
        pub fn $fnname() -> $crate::Result<$fnret> {
            let $param = ::std::env::var($varname)?;
            $code
        }

        inputs!($($rest)*);
    };

    ($(#[$attr:meta])* fn $fnname:ident -> $fnret:ty, $varname:expr, parse $kind:ident; $($rest:tt)*) => {
        inputs! {
            $(#[$attr])*
            fn $fnname -> $fnret, $varname,
               |s| s.parse().map_err(|e| $crate::Error::from_kind($crate::ErrorKind::$kind(e)));
                    // FIXME weird error-chain incantation (is $kind necessary?)
            
            $($rest)*
        }
    };

    ($(#[$attr:meta])* fn $fnname:ident -> $fnret:ty, $varname:expr; $($rest:tt)*) => {
        inputs! {
            $(#[$attr])*
            fn $fnname -> $fnret, $varname, |x| ::std::result::Result::Ok(x);

            $($rest)*
        }
    };
}

macro_rules! outputs {
    () => {};

    (@inner [($($fndecl:tt)*) ($(($($gen:tt)*))*) ($($rest:tt)*)]
            () -> ($(, $n:ident : $t:ty)*)
                  ($($strings:expr),*)
                  ($($vars:tt)*)) => {
        $($fndecl)*<$($($gen)*),*>($($n: $t),*) {
            println!(concat!($($strings),*) $($vars)*);
        }

        outputs!($($rest)*);
    };

    (@inner [$fndecl:tt ($($gen:tt)*) $rest:tt]
            (<$typ:ident> $(,)*) -> ($($params:tt)*)
                                 ($($strings:tt)*)
                                 ($($vars:tt)*)) => {
        outputs!(@inner [$fndecl ($($gen)* ($typ: ::std::convert::AsRef<::std::ffi::OsStr> + ?::std::marker::Sized)) $rest]
                        () -> ($($params)*, n: &$typ)
                              ($($strings)*, "{}")
                              ($($vars)*, ::std::path::Path::new(n).display()));
    };
    (@inner $thru:tt
            ($typ:ty $(,)*) -> ($($params:tt)*)
                               ($($strings:tt)*)
                               ($($vars:tt)*)) => {
        outputs!(@inner $thru
                        () -> ($($params)*, n: $typ)
                              ($($strings)*, "{}")
                              ($($vars)*, n));
    };
    (@inner [$fndecl:tt ($($gen:tt)*) $rest:tt]
            (<$typ:ident>, $($types:tt)*) -> ($($params:tt)*)
                                             ($($strings:tt)*)
                                             ($($vars:tt)*)) => {
        outputs!(@inner [$fndecl ($($gen)* ($typ: ::std::convert::AsRef<::std::ffi::OsStr> + ?::std::marker::Sized)) $rest]
                        ($($types),*) -> ($($params)*, n: &$typ)
                                         ($($strings)*, "{}", "=")
                                         ($($vars)*, ::std::path::Path::new(n).display()));
    };
    (@inner $thru:tt
            ($typ:ty, $($types:tt)*) -> ($($params:tt)*)
                                        ($($strings:tt)*)
                                        ($($vars:tt)*)) => {
        outputs!(@inner $thru
                        ($($types)*) -> ($($params)*, n: $typ)
                                        ($($strings)*, "{}", "=")
                                        ($($vars)*, n));
    };

    ($(#[$attr:meta])* fn $fnname:ident($($types:tt)*) => None; $($rest:tt)*) => {
        outputs!(@inner [($(#[$attr])* pub fn $fnname) () ($($rest)*)]
                        ($($types)*,) -> ()
                                         ("cargo:")
                                         ());
    };

    ($(#[$attr:meta])* fn $fnname:ident($($types:tt)*) => $string:expr; $($rest:tt)*) => {
        outputs!(@inner [($(#[$attr])* pub fn $fnname) () ($($rest)*)]
                        ($($types)*,) -> ()
                                         ("cargo:", $string, "=")
                                         ());
    };
}