limen-build 0.1.0-alpha.1

Proc-macro wrapper for limen-codegen.
Documentation
// Copyright © 2025–present Arlo Louis Byrne (idky137)
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0.
// See the LICENSE-APACHE file in the project root for license terms.

#![warn(missing_docs)]
#![deny(unsafe_code)]
//! # limen-build — proc-macro front for `limen-codegen`
//!
//! Provides the `define_graph! { ... }` macro which parses the Limen graph DSL
//! and expands it into fully-typed Rust code via `limen-codegen`.
//!
//! See `limen-codegen` crate docs for the DSL shape and the exact generated API.

extern crate proc_macro;

use proc_macro::TokenStream;
use proc_macro2::{Literal, TokenStream as TokenStream2};
use quote::quote;

/// Define a Limen graph using the DSL and expand it into generated Rust code.
///
/// This macro forwards its input tokens directly to
/// [`limen_codegen::expand_tokens`] and returns the emitted items.
///
/// Each invocation emits a single concrete graph type. Use the trailing
/// `concurrent;` keyword to additionally request the std-only
/// `ScopedGraphApi` implementation for that same graph type.
///
/// # Example (no_std, `GraphApi` only)
/// ```ignore
/// use limen_build::define_graph;
///
/// define_graph! {
///     pub struct MyGraph;
///
///     nodes {
///         0: { ty: my::Src,  in_ports: 0, out_ports: 1, in_payload: (),  out_payload: u32, name: Some("src"), ingress_policy: MY_Q },
///         1: { ty: my::Map,  in_ports: 1, out_ports: 1, in_payload: u32, out_payload: u32, name: Some("map") },
///         2: { ty: my::Sink, in_ports: 1, out_ports: 0, in_payload: u32, out_payload: (),  name: Some("sink") },
///     }
///
///     edges {
///         0: { ty: TestSpscRingBuf<8>, payload: u32, manager: StaticMemoryManager<u32, 8>, from: (0, 0), to: (1, 0), policy: EDGE_POL, name: Some("src->map") },
///         1: { ty: TestSpscRingBuf<8>, payload: u32, manager: StaticMemoryManager<u32, 8>, from: (1, 0), to: (2, 0), policy: EDGE_POL, name: Some("map->sink") },
///     }
/// }
/// ```
///
/// # Example (std, `GraphApi` + `ScopedGraphApi`)
///
/// Adding `concurrent;` inside the **same** invocation extends the generated graph type
/// with a std-only `ScopedGraphApi` impl — it does **not** emit a second graph type.
///
/// ```ignore
/// use limen_build::define_graph;
///
/// define_graph! {
///     pub struct MyGraph;
///
///     nodes {
///         0: { ty: my::Src,  in_ports: 0, out_ports: 1, in_payload: (),  out_payload: u32, name: Some("src"), ingress_policy: MY_Q },
///         1: { ty: my::Map,  in_ports: 1, out_ports: 1, in_payload: u32, out_payload: u32, name: Some("map") },
///         2: { ty: my::Sink, in_ports: 1, out_ports: 0, in_payload: u32, out_payload: (),  name: Some("sink") },
///     }
///
///     edges {
///         0: { ty: TestSpscRingBuf<8>, payload: u32, manager: ConcurrentMemoryManager<u32>, from: (0, 0), to: (1, 0), policy: EDGE_POL, name: Some("src->map") },
///         1: { ty: TestSpscRingBuf<8>, payload: u32, manager: ConcurrentMemoryManager<u32>, from: (1, 0), to: (2, 0), policy: EDGE_POL, name: Some("map->sink") },
///     }
///
///     concurrent;
/// }
/// ```
#[proc_macro]
pub fn define_graph(input: TokenStream) -> TokenStream {
    let tokens2 = TokenStream2::from(input);
    match limen_codegen::expand_tokens(tokens2) {
        Ok(out) => out.into(),
        Err(err) => error_to_tokens(err).into(),
    }
}

/// Convert a `CodegenError` into a compile-time error token stream.
///
/// - For parse errors, preserve spans using `syn::Error::to_compile_error`.
/// - For other errors, use `compile_error!("<message>")`.
fn error_to_tokens(err: limen_codegen::CodegenError) -> TokenStream2 {
    match err {
        limen_codegen::CodegenError::Parse(e) => e.to_compile_error(),
        other => {
            let lit = Literal::string(&other.to_string());
            quote! { compile_error!(#lit); }
        }
    }
}