mod common;

use milter::*;
use std::net::SocketAddr;

#[test]
fn data_storage() {
    let test_name = common::test_name(file!());
    let miltertest = common::spawn_miltertest_runner(file!());

    Milter::new("inet:3336@localhost")
        .name(test_name.to_str().unwrap())
        .on_connect(connect_callback)
        .on_mail(mail_callback)
        .on_rcpt(rcpt_callback)
        .on_close(close_callback)
        .run()
        .expect("milter execution failed");

    let exit_code = miltertest.join().expect("panic in miltertest runner");
    assert!(exit_code.success(), "miltertest returned error exit code");
}

#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
struct Data {
    num: usize,
    text: String,
}

#[on_connect(connect_callback)]
fn handle_connect(mut ctx: Context<Data>, _: &str, _: Option<SocketAddr>) -> milter::Result<Status> {
    let data = Data {
        num: 1,
        text: "connect".into(),
    };

    assert_eq!(ctx.data.replace(data)?, None);

    Ok(Status::Continue)
}

#[on_mail(mail_callback)]
fn handle_mail(mut ctx: Context<Data>, _: Vec<&str>) -> milter::Result<Status> {
    let new_data = Data {
        num: 2,
        text: "mail".into(),
    };

    let old_data = ctx.data.replace(new_data)?.unwrap();

    assert_eq!(old_data.num, 1);
    assert_eq!(old_data.text, "connect");

    Ok(Status::Continue)
}

#[on_rcpt(rcpt_callback)]
fn handle_rcpt(mut ctx: Context<Data>, _: Vec<&str>) -> milter::Result<Status> {
    {
        let mut data = ctx.data.borrow_mut().unwrap();
        data.num = 3;
        data.text.push_str("rcpt");
    }

    let data = ctx.data.borrow().unwrap();

    assert_eq!(data.num, 3);
    assert_eq!(data.text, "mailrcpt");

    Ok(Status::Continue)
}

#[on_close(close_callback)]
fn handle_close(mut ctx: Context<Data>) -> milter::Result<Status> {
    let data = ctx.data.take()?.unwrap();

    assert_eq!(data.num, 3);
    assert_eq!(data.text, "mailrcpt");

    Ok(Status::Continue)
}
