//! Proc macros for `mizan-core`. See sibling modules for each macro's body.
//!
//! Consumer code reads:
//!   ```ignore
//!   use mizan_core::prelude::*;
//!   pub use mizan_core as mizan;  // so `#[mizan::context]` / `#[mizan::client]` read naturally
//!
//!   #[derive(Mizan, serde::Serialize, serde::Deserialize)]
//!   pub struct ProfileOutput { pub user_id: i64, pub name: String }
//!
//!   #[mizan::context("user")]
//!   pub struct UserCtx;
//!
//!   #[mizan::client(context = UserCtx)]
//!   pub async fn user_profile(req: &Request, user_id: i64) -> ProfileOutput { ... }
//!   ```
//!
//! The function macro is named `client` to mirror Python's `@client`
//! decorator and to keep the namespace `mizan::` purely a module path —
//! `#[mizan(...)]` would collide with `mizan::context` (a module path
//! can't simultaneously be a callable macro in Rust).

mod context;
mod derive;
mod function;
mod shape;

use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput, ItemFn, ItemStruct};

#[proc_macro_derive(Mizan)]
pub fn derive_mizan(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    derive::expand(input).into()
}

#[proc_macro_attribute]
pub fn context(attr: TokenStream, item: TokenStream) -> TokenStream {
    let args = match context::ContextArgs::parse(attr.into()) {
        Ok(a) => a,
        Err(e) => return e.to_compile_error().into(),
    };
    let item = parse_macro_input!(item as ItemStruct);
    context::expand(args, item).into()
}

/// The function-registration attribute macro. Used as `#[mizan::client]`
/// (no args) or `#[mizan::client(context = X, affects = Y, merge = Z,
/// websocket, private)]`.
#[proc_macro_attribute]
pub fn client(attr: TokenStream, item: TokenStream) -> TokenStream {
    let args = match function::FunctionArgs::parse(attr.into()) {
        Ok(a) => a,
        Err(e) => return e.to_compile_error().into(),
    };
    let item = parse_macro_input!(item as ItemFn);
    function::expand(args, item).into()
}
