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
//! `std::experiments` module for the [Rune Language].
//!
//! [Rune Language]: https://rune-rs.github.io
//!
//! ## Usage
//!
//! Add the following to your `Cargo.toml`:
//!
//! ```toml
//! rune-modules = {version = "0.9.1", features = ["test"]}
//! ```
//!
//! Install it into your context:
//!
//! ```rust
//! # fn main() -> runestick::Result<()> {
//! let mut context = runestick::Context::with_default_modules()?;
//! context.install(&rune_modules::test::module(true)?)?;
//! # Ok(())
//! # }
//! ```

use rune::T;
use rune::ast;
use rune::macros;
use rune::{quote, Parser, TokenStream};

/// Construct the `std::test` module.
pub fn module(_stdio: bool) -> Result<runestick::Module, runestick::ContextError> {
    let mut module = runestick::Module::with_crate_item("std", &["test"]);
    module.macro_(&["assert"], assert_macro)?;
    module.macro_(&["assert_eq"], assert_eq_macro)?;
    Ok(module)
}

/// Implementation for the `assert!` macro.
pub(crate) fn assert_macro(
    stream: &TokenStream,
) -> runestick::Result<TokenStream> {
    let mut p = Parser::from_token_stream(stream);
    let expr = p.parse::<ast::Expr>()?;

    let message = if p.parse::<Option<T![,]>>()?.is_some() {
        p.parse_all::<Option<macros::FormatArgs>>()?
    } else {
        None
    };

    let output = if let Some(message) = &message {
        let expanded = message.expand()?;

        quote!(if !(#expr) {
            panic("assertion failed: " + (#expanded));
        })
    } else {
        let message = format!("assertion failed: {}", macros::stringify(&expr));
        let message = ast::Lit::new(&message);

        quote!(if !(#expr) {
            panic(#message);
        })
    };

    Ok(output.into_token_stream())
}

/// Implementation for the `assert!` macro.
pub(crate) fn assert_eq_macro(
    stream: &TokenStream,
) -> runestick::Result<TokenStream> {
    let mut p = Parser::from_token_stream(stream);
    let left = p.parse::<ast::Expr>()?;
    p.parse::<T![,]>()?;
    let right = p.parse::<ast::Expr>()?;

    let message = if p.parse::<Option<T![,]>>()?.is_some() {
        p.parse_all::<Option<macros::FormatArgs>>()?
    } else {
        None
    };

    let output = if let Some(message) = &message {
        let message = message.expand()?;

        quote! {{
            let left = #left;
            let right = #right;

            if !(left == right) {
                let message = #message;
                message += format!("\nleft: {:?}", left);
                message += format!("\nright: {:?}", right);
                panic("assertion failed (left == right): " + message);
            }
        }}
    } else {
        let message = format!("assertion failed (left == right):");
        let message = ast::Lit::new(&message);

        quote! {{
            let left = #left;
            let right = #right;

            if !(left == right) {
                let message = String::from_str(#message);
                message += format!("\nleft: {:?}", left);
                message += format!("\nright: {:?}", right);
                panic(message);
            }
        }}
    };

    Ok(output.into_token_stream())
}