fpgad_macros 0.5.1

Procedural macros for the FPGAd project
Documentation
#![doc = include_str!("../README.md")]

use proc_macro::TokenStream;
use quote::quote;
use syn::{Expr, ItemStruct, Lit, Meta, parse_macro_input, punctuated::Punctuated};

/// Procedural macro to generate platform registration code.
///
/// This macro adds a `register_platform()` method to a struct that registers it
/// with the global platform registry. The macro requires a `compat_string` parameter
/// that specifies the device tree compatibility string(s) the platform supports.
///
/// The macro also generates a `COMPAT_STRING` constant that contains the compatibility string.
///
/// **Important**: Each platform MUST define an `is_available()` function that returns `bool`.
/// - For built-in platforms (in `platforms` dir): `pub fn is_available() -> bool { true }`
/// - For softeners (in `softeners` dir): Custom logic checking if dependencies exist
///
/// # Arguments
///
/// * `compat_string` - Comma-separated device tree compatibility strings
///
/// # Generated Code
///
/// The macro generates:
/// ```rust,ignore
/// impl YourStruct {
///     #[doc(hidden)]
///     pub fn register_platform() {
///         crate::platforms::platform::register_platform(
///             "compat_string",
///             || Box::new(Self::new()),
///             Self::is_available
///         );
///     }
///
///     pub const COMPAT_STRING: &'static str = "compat_string";
/// }
/// ```
///
/// # Usage
///
/// Built-in platform (always available):
/// ```rust,ignore
/// #[platform(compat_string = "xlnx-sys")]
/// pub struct XilinxSysPlatform { }
///
/// impl XilinxSysPlatform {
///     pub fn new() -> Self { Self }
///
///     pub fn is_available() -> bool {
///         true  // Always available
///     }
/// }
/// ```
///
/// Softener with custom availability:
/// ```rust,ignore
/// #[platform(compat_string = "xlnx,dfx-mgr")]
/// pub struct MyPlatform { }
///
/// impl MyPlatform {
///     pub fn new() -> Self { Self }
///
///     pub fn is_available(&self) -> bool {
///         // Check if required binary exists
///         std::path::Path::new("/usr/bin/dfx-mgr-client").exists()
///     }
/// }
/// ```
#[proc_macro_attribute]
pub fn platform(args: TokenStream, input: TokenStream) -> TokenStream {
    // Parse the struct
    let input_struct = parse_macro_input!(input as ItemStruct);
    let struct_name = &input_struct.ident;

    // Parse the attribute arguments
    let args = parse_macro_input!(args with Punctuated::<Meta, syn::Token![,]>::parse_terminated);

    let mut compat_string: Option<String> = None;
    for arg in args {
        if let Meta::NameValue(nv) = arg
            && nv.path.is_ident("compat_string")
            && let Expr::Lit(ref lit_expr) = nv.value
            && let Lit::Str(ref litstr) = lit_expr.lit
        {
            compat_string = Some(litstr.value());
        }
    }
    let compat_string = compat_string.expect("compat_string must be provided");

    // Generate code to register the platform.
    // The platform impl MUST define is_available() -> bool
    let expanded = quote! {
        #input_struct

        impl #struct_name {
            #[doc(hidden)]
            pub fn register_platform() {
                crate::platforms::platform::register_platform(
                    #compat_string,
                    || Box::new(Self::new())
                );
            }

            /// The device tree compatibility string(s) supported by this platform.
            ///
            /// This constant is automatically generated by the `#[platform]` macro
            /// and can be referenced in the `Platform` trait implementation.
            pub const COMPAT_STRING: &'static str = #compat_string;
        }
    };
    TokenStream::from(expanded)
}