//! Various derive macros for oxidd
//!
//! Note that the code generated by the macros depends on definitions in
//! `oxidd_core`, not their re-exports in `oxidd`. So in case you want to use
//! these macros as an "external" user, you may need to add `oxidd_core` to your
//! `Cargo.toml`.
#![warn(missing_docs)]

mod countable;
mod function;
mod manager_event_subscriber;
pub(crate) mod util;

// The actual proc macros must reside at the root of the crate:

/// Derive the `Countable` trait from `oxidd_core`
///
/// The `Countable` trait can be derived for:
/// - Fieldless `enum`s with primitive representation (currently `repr(u8)`
///   only). Explicit discriminants are not yet supported.
/// - `struct`s with zero fields.
#[proc_macro_error::proc_macro_error]
#[proc_macro_derive(Countable)]
pub fn derive_countable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    let expanded = countable::derive_countable(input);
    proc_macro::TokenStream::from(expanded)
}

/// Derive the `ManagerEventSubscriber` trait from `oxidd_core::util`
///
/// # Example
///
/// ```
/// # use std::marker::PhantomData;
/// # use oxidd_core::{Manager, ManagerEventSubscriber};
/// # use oxidd_derive::ManagerEventSubscriber;
/// # struct ApplyCache<M>(PhantomData<M>);
/// # impl<M: Manager> ManagerEventSubscriber<M> for ApplyCache<M> {}
/// # struct ZBDDCache<M>(PhantomData<M>);
/// # impl<M: Manager> ManagerEventSubscriber<M> for ZBDDCache<M> {}
/// #[derive(ManagerEventSubscriber)]
/// #[subscribe(manager = M)]
/// struct ManagerData<M> {
///     apply_cache: ApplyCache<M>,
///     zbdd_cache: ZBDDCache<M>,
///     #[subscribe(skip)]
///     field_to_skip: bool,
/// }
/// ```
///
/// Specifying the manager type via `#[subscribe(manager = M)]` is mandatory (it
/// does not need to be a generic parameter, though). The generated
/// implementation will look like this:
///
/// ```
/// # use std::marker::PhantomData;
/// # use oxidd_core::{Manager, ManagerEventSubscriber};
/// # struct ApplyCache<M>(PhantomData<M>);
/// # impl<M: Manager> ManagerEventSubscriber<M> for ApplyCache<M> {}
/// # struct ZBDDCache<M>(PhantomData<M>);
/// # impl<M: Manager> ManagerEventSubscriber<M> for ZBDDCache<M> {}
/// # struct ManagerData<M> {
/// #     apply_cache: ApplyCache<M>,
/// #     zbdd_cache: ZBDDCache<M>,
/// #     field_to_skip: bool,
/// # }
/// impl<M> ManagerEventSubscriber<M> for ManagerData<M>
/// where
///     M: Manager,
///     ApplyCache<M>: ManagerEventSubscriber<M>,
///     ZBDDCache<M>: ManagerEventSubscriber<M>,
/// {
///     fn pre_gc(&self, manager: &M) {
///         self.apply_cache.pre_gc(manager);
///         self.zbdd_cache.pre_gc(manager);
///     }
///     unsafe fn post_gc(&self, manager: &M) {
///         // SAFETY: since the caller ensures to call this method (only!) once
///         // after a GC, we call `post_gc` (only) once  for each substructure.
///         unsafe {
///             self.apply_cache.post_gc(manager);
///             self.zbdd_cache.post_gc(manager);
///         }
///     }
///     // similar implementations for `pre_reorder` and `post_reorder` ...
///     fn pre_reorder_mut(manager: &mut M) {
///         ApplyCache::<M>::pre_reorder_mut(manager);
///         ZBDDCache::<M>::pre_reorder_mut(manager);
///     }
///     // similar implementation for `post_reorder_mut` ...
/// }
/// ```
///
/// Note that if you have two fields of the same type (and you do not use
/// `#[subscribe(skip)]` on either of these fields), the `pre_reorder_mut` and
/// `post_reorder_mut` implementation will call the method for that type twice
/// (with the same `manager` argument).
///
/// In some cases the trait bounds `M: Manager`,
/// `ApplyCache<M>: ManagerEventSubscriber<M>`, etc. can lead to undesired
/// self-references and cause the [compiler error
/// E0275](https://doc.rust-lang.org/stable/error_codes/E0275.html). To disable
/// the generation of those trait bounds, specify `no_trait_bounds` via a
/// struct-level `subscribe` attribute. (This will not affect trait bounds from
/// the struct declaration itself.)
#[proc_macro_error::proc_macro_error]
#[proc_macro_derive(ManagerEventSubscriber, attributes(subscribe))]
pub fn derive_manager_event_subscriber(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    let expanded = manager_event_subscriber::derive_manager_event_subscriber(input);
    proc_macro::TokenStream::from(expanded)
}

/// Derive the `Function` trait from `oxidd_core::function`
///
/// This trait can be derived for `struct`s that wrap another `Function`
/// implementation. Currently, this macro is restricted to `struct`s with a
/// single field.
///
/// You may specify the associated `ManagerRef` type using an attribute
/// `#[use_manager_ref(YourManagerRefType)]`.
///
/// To influence the representation identifier, use the attribute
/// `#[repr_id = "MY_BDD"]`.
#[proc_macro_error::proc_macro_error]
#[proc_macro_derive(Function, attributes(use_manager_ref, repr_id))]
pub fn derive_function(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    let expanded = function::derive_function(input);
    proc_macro::TokenStream::from(expanded)
}

/// Derive the `FunctionSubst` trait from `oxidd_core::function`
///
/// This trait can be derived for `struct`s that wrap another `FunctionSubst`
/// implementation. The same restrictions apply as for deriving [`Function`].
#[proc_macro_error::proc_macro_error]
#[proc_macro_derive(FunctionSubst)]
pub fn derive_function_subst(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    let expanded = function::derive_function_subst(input);
    proc_macro::TokenStream::from(expanded)
}

/// Derive the `BooleanFunction` trait from `oxidd_core::function`
///
/// This trait can be derived for `struct`s that wrap another `BooleanFunction`
/// implementation. The same restrictions apply as for deriving [`Function`].
#[proc_macro_error::proc_macro_error]
#[proc_macro_derive(BooleanFunction)]
pub fn derive_boolean_function(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    let expanded = function::derive_boolean_function(input);
    proc_macro::TokenStream::from(expanded)
}

/// Derive the `BooleanFunctionQuant` trait from `oxidd_core::function`
///
/// This trait can be derived for `struct`s that wrap another
/// `BooleanFunctionQuant` implementation. The same restrictions apply as for
/// deriving [`Function`].
#[proc_macro_error::proc_macro_error]
#[proc_macro_derive(BooleanFunctionQuant)]
pub fn derive_boolean_function_quant(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    let expanded = function::derive_boolean_function_quant(input);
    proc_macro::TokenStream::from(expanded)
}

/// Derive the `BooleanVecSet` trait from `oxidd_core::function`
///
/// This trait can be derived for `struct`s that wrap another `BooleanVecSet`
/// implementation. The same restrictions apply as for deriving [`Function`].
#[proc_macro_error::proc_macro_error]
#[proc_macro_derive(BooleanVecSet)]
pub fn derive_boolean_vec_set(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    let expanded = function::derive_boolean_vec_set(input);
    proc_macro::TokenStream::from(expanded)
}

/// Derive the `PseudoBooleanFunction` trait from `oxidd_core::function`
///
/// This trait can be derived for `struct`s that wrap another
/// `PseudoBooleanFunction` implementation. The same restrictions apply as for
/// deriving [`Function`].
#[proc_macro_error::proc_macro_error]
#[proc_macro_derive(PseudoBooleanFunction)]
pub fn derive_pseudo_boolean_function(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    let expanded = function::derive_pseudo_boolean_function(input);
    proc_macro::TokenStream::from(expanded)
}

/// Derive the `TVLFunction` trait from `oxidd_core::function`
///
/// This trait can be derived for `struct`s that wrap another `TVLFunction`
/// implementation. The same restrictions apply as for deriving [`Function`].
#[proc_macro_error::proc_macro_error]
#[proc_macro_derive(TVLFunction)]
pub fn derive_tvl_function(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    let expanded = function::derive_tvl_function(input);
    proc_macro::TokenStream::from(expanded)
}
