//! ## Overview
//!
//! Procedural macros for use with the `esp-hal` family of HAL packages. In
//! general, you should not need to depend on this package directly, as the
//! relevant procmacros are re-exported by the various HAL packages.
//!
//! Provides macros for:
//!
//! - Placing statics and functions into RAM
//! - Marking interrupt handlers
//! - Blocking and Async `#[main]` macros
//!
//! These macros offer developers a convenient way to control memory placement
//! and define interrupt handlers in their embedded applications, allowing for
//! optimized memory usage and precise handling of hardware interrupts.
//!
//! Key Components:
//!  - [`handler`](macro@handler) - Attribute macro for marking interrupt
//!    handlers. Interrupt handlers are used to handle specific hardware
//!    interrupts generated by peripherals.
//!
//!  - [`ram`](macro@ram) - Attribute macro for placing statics and functions
//!    into specific memory sections, such as SRAM or RTC RAM (slow or fast)
//!    with different initialization options. See its documentation for details.
//!
//!  - [`embassy::main`](macro@embassy_main) - Creates a new `executor` instance
//!    and declares an application entry point spawning the corresponding
//!    function body as an async task.
//!
//! ## Examples
//!
//! #### `main` macro
//!
//! Requires the `embassy` feature to be enabled.
//!
//! ```rust, no_run
//! #[main]
//! async fn main(spawner: Spawner) {
//!     // Your application's entry point
//! }
//! ```
//!
//! ## Feature Flags
#![doc = document_features::document_features!()]
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]

use proc_macro::TokenStream;

mod blocking;
mod builder;
#[cfg(feature = "embassy")]
mod embassy;
mod interrupt;
#[cfg(any(
    feature = "is-lp-core",
    feature = "is-ulp-core",
    feature = "has-lp-core",
    feature = "has-ulp-core"
))]
mod lp_core;
mod ram;

/// Sets which segment of RAM to use for a function or static and how it should
/// be initialized.
///
/// Requires the `ram` feature.
///
/// # Options
///
/// - `rtc_fast`: Use RTC fast RAM.
/// - `rtc_slow`: Use RTC slow RAM. **Note**: not available on all targets.
/// - `persistent`: Persist the contents of the `static` across resets. See [the
///   section below](#persistent) for details.
/// - `zeroed`: Initialize the memory of the `static` to zero. The initializer
///   expression will be discarded. Types used must implement
///   [`bytemuck::Zeroable`].
///
/// Using both `rtc_fast` and `rtc_slow` or `persistent` and `zeroed` together
/// is an error.
///
/// ## `persistent`
///
/// Initialize the memory to zero after the initial boot. Thereafter,
/// initialization is skipped to allow communication across `software_reset()`,
/// deep sleep, watchdog timeouts, etc.
///
/// Types used must implement [`bytemuck::AnyBitPattern`].
///
/// ### Warnings
///
/// - A system-level or lesser reset occurring before the ram has been zeroed
///   *could* skip initialization and start the application with the static
///   filled with random bytes.
/// - There is no way to keep some kinds of resets from happening while updating
///   a persistent static—not even a critical section.
///
/// If these are issues for your application, consider adding a checksum
/// alongside the data.
///
/// # Examples
///
/// ```rust, no_run
/// #[ram(rtc_fast)]
/// static mut SOME_INITED_DATA: [u8; 2] = [0xaa, 0xbb];
///
/// #[ram(rtc_fast, persistent)]
/// static mut SOME_PERSISTENT_DATA: [u8; 2] = [0; 2];
///
/// #[ram(rtc_fast, zeroed)]
/// static mut SOME_ZEROED_DATA: [u8; 8] = [0; 8];
/// ```
///
/// See the `ram` example in the esp-hal repository for a full usage example.
///
/// [`bytemuck::AnyBitPattern`]: https://docs.rs/bytemuck/1.9.0/bytemuck/trait.AnyBitPattern.html
/// [`bytemuck::Zeroable`]: https://docs.rs/bytemuck/1.9.0/bytemuck/trait.Zeroable.html
#[proc_macro_attribute]
#[proc_macro_error2::proc_macro_error]
pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
    ram::ram(args, input)
}

/// Mark a function as an interrupt handler.
///
/// Optionally a priority can be specified, e.g. `#[handler(priority =
/// esp_hal::interrupt::Priority::Priority2)]`.
///
/// If no priority is given, `Priority::min()` is assumed
#[proc_macro_attribute]
#[proc_macro_error2::proc_macro_error]
pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream {
    interrupt::handler(args, input)
}

/// Load code to be run on the LP/ULP core.
///
/// ## Example
/// ```rust, no_run
/// let lp_core_code = load_lp_code!("path.elf");
/// lp_core_code.run(&mut lp_core, lp_core::LpCoreWakeupSource::HpCpu, lp_pin);
/// ````
#[cfg(any(feature = "has-lp-core", feature = "has-ulp-core"))]
#[proc_macro]
pub fn load_lp_code(input: TokenStream) -> TokenStream {
    lp_core::load_lp_code(input)
}

/// Marks the entry function of a LP core / ULP program.
#[cfg(any(feature = "is-lp-core", feature = "is-ulp-core"))]
#[proc_macro_attribute]
#[proc_macro_error2::proc_macro_error]
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
    lp_core::entry(args, input)
}

/// Creates a new `executor` instance and declares an application entry point
/// spawning the corresponding function body as an async task.
///
/// The following restrictions apply:
///
/// * The function must accept exactly 1 parameter, an
///   `embassy_executor::Spawner` handle that it can use to spawn additional
///   tasks.
/// * The function must be declared `async`.
/// * The function must not use generics.
/// * Only a single `main` task may be declared.
///
/// ## Examples
/// Spawning a task:
///
/// ``` rust
/// #[main]
/// async fn main(_s: embassy_executor::Spawner) {
///     // Function body
/// }
/// ```
#[cfg(feature = "embassy")]
#[proc_macro_attribute]
pub fn embassy_main(args: TokenStream, item: TokenStream) -> TokenStream {
    embassy::main(args, item)
}

/// Attribute to declare the entry point of the program
///
/// The specified function will be called by the reset handler *after* RAM has
/// been initialized. If present, the FPU will also be enabled before the
/// function is called.
///
/// The type of the specified function must be `[unsafe] fn() -> !` (never
/// ending function)
///
/// # Properties
///
/// The entry point will be called by the reset handler. The program can't
/// reference to the entry point, much less invoke it.
///
/// # Examples
///
/// - Simple entry point
///
/// ``` no_run
/// #[main]
/// fn main() -> ! {
///     loop { /* .. */ }
/// }
/// ```
#[proc_macro_attribute]
pub fn blocking_main(args: TokenStream, input: TokenStream) -> TokenStream {
    blocking::main(args, input)
}

/// Automatically implement the [Builder Lite] pattern for a struct.
///
/// This will create an `impl` which contains methods for each field of a
/// struct, allowing users to easily set the values. The generated methods will
/// be the field name prefixed with `with_`, and calls to these methods can be
/// chained as needed.
///
/// ## Example
///
/// ```rust, no_run
/// #[derive(Default)]
/// enum MyEnum {
///     #[default]
///     A,
///     B,
/// }
///
/// #[derive(Default, BuilderLite)]
/// #[non_exhaustive]
/// struct MyStruct {
///     enum_field: MyEnum,
///     bool_field: bool,
///     option_field: Option<i32>,
/// }
///
/// MyStruct::default()
///     .with_enum_field(MyEnum::B)
///     .with_bool_field(true)
///     .with_option_field(-5);
/// ```
///
/// [Builder Lite]: https://matklad.github.io/2022/05/29/builder-lite.html
#[proc_macro_derive(BuilderLite, attributes(builder_lite))]
pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
    builder::builder_lite_derive(item)
}
