// 引数をそのまま展開するマクロ
macro_rules! as_is {
    ($arg:expr) => {
        $arg
    };
}

// 2つの引数を加算する式に展開するマクロ
macro_rules! add_two {
    ($arg1:expr, $arg2:expr) => {
        ($arg1) + ($arg2)
    };
}

// C言語のforループのような構文を提供するマクロ
macro_rules! cfor {
    ($init:stmt; $cond:expr; $step:stmt; $body:block) => {
        {
            $init
            while $cond {
                $body
                $step
            }
        }
    };
}

// 可変引数を受け取るマクロ
macro_rules! print_args {
    () => {
        println!("print_args!に引数がありません。");
    };
    ($($arg:expr),*) => {
        {
            $(
                println!("print_args!の引数: {}", $arg);
            )*
        }
    };
}

// ゲッターを実装するマクロ
macro_rules! implement_getter {
    ($struct_name:ident, $field_name:ident, $field_type:ty) => {
        impl $struct_name {
            pub fn get_$field_name(&self) -> $field_type {
                self.$field_name
            }
        }
    };
}

// 不衛生なマクロ
macro_rules! initialize_x {
    () => {
        x = 42;
    };
}

// 不衛生なマクロ
macro_rules! declare_variable {
    () => {
        let inaccessible = 0;
    };
}

fn main() {
    println!("as_is!(1 + 1) = {}", as_is!(1 + 1));

    println!("add_two!(3, 5) = {}", add_two!(3, 5));

    cfor!(let mut i = 0; i < 5; i += 1; {
        println!("i = {}", i);
    });

    print_args!("日", "月", "火");
    print_args!["Sunday", "Monday"];
    print_args!{};

    struct Person {
        name: String,
        age: u32,
    }
    implement_getter!(Person, name, String);
    implement_getter!(Person, age, u32);
    let person = Person { name: "Taro".to_string(), age: 30 };
    println!("Person: {}, {}", person.get_name(), person.get_age());

    println!("as_is!(1 + 1) * 3 = {}", as_is!(1 + 1) * 3);

    // コメントアウトを解除するとコンパイルエラーになります。
    //let mut x;
    //initialize_x!();

    // コメントアウトを解除するとコンパイルエラーになります。
    //declare_variable!();
    //println!("inaccessible = {}", inaccessible);
}
