dusk_forge_contract/lib.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7//! Procedural macro for the `#[contract]` attribute.
8//!
9//! This macro is applied to a module containing a contract struct and its
10//! impl block. It extracts metadata about public methods and events, and
11//! generates a `CONTRACT_SCHEMA` constant plus extern "C" wrappers.
12//!
13//! # Pipeline
14//!
15//! 1. [`parse::analyze`] walks the user module and produces an
16//! [`parse::Analysis`] (functions, registered events, imports, contract
17//! identifier).
18//! 2. [`generate`] / [`data_driver`] consume the analysis and emit the contract
19//! or data-driver bindings.
20//!
21//! `lib.rs` only orchestrates these two phases — all walking, validation,
22//! and IR construction lives in the `parse` module.
23//!
24//! # Example
25//!
26//! ```ignore
27//! #[contract]
28//! mod my_contract {
29//! use my_crate::MyType;
30//! use dusk_core::abi;
31//!
32//! pub struct MyContract {
33//! value: u64,
34//! }
35//!
36//! impl MyContract {
37//! pub fn set_value(&mut self, value: MyType) {
38//! // ...
39//! }
40//! }
41//! }
42//! ```
43
44#![deny(missing_docs)]
45#![deny(rustdoc::broken_intra_doc_links)]
46#![deny(unused_must_use)]
47#![deny(unused_extern_crates)]
48#![deny(clippy::pedantic)]
49#![warn(missing_debug_implementations, unreachable_pub, rustdoc::all)]
50
51mod data_driver;
52mod generate;
53mod parse;
54mod resolve;
55mod validate;
56
57use proc_macro::TokenStream;
58use syn::{ItemMod, parse_macro_input};
59
60/// The main contract proc macro.
61///
62/// Applied to a module containing a contract struct and impl block.
63/// Extracts metadata and generates schema + extern wrappers.
64///
65/// # Errors
66///
67/// This macro will produce compile errors if:
68/// - The module has no content (just a declaration like `mod foo;`)
69/// - The module contains glob imports (`use foo::*`)
70/// - The module contains relative imports (`use self::`, `use super::`, `use
71/// crate::`)
72/// - The module contains multiple `pub struct` declarations
73/// - The module contains no `pub struct`
74/// - The module contains no impl block for the contract struct
75/// - A public method has no `self` receiver (associated functions)
76/// - A public method has generic type or const parameters
77/// - A public method is async
78/// - A public method consumes `self` instead of borrowing it
79/// - A public method uses `impl Trait` in parameters or return type
80#[proc_macro_attribute]
81pub fn contract(attr: TokenStream, item: TokenStream) -> TokenStream {
82 let module = parse_macro_input!(item as ItemMod);
83
84 let registered_events = match parse::events::module_events(attr.into()) {
85 Ok(events) => events,
86 Err(e) => return e.to_compile_error().into(),
87 };
88
89 let Some((_, items)) = &module.content else {
90 return syn::Error::new_spanned(&module, "#[contract] requires a module with content")
91 .to_compile_error()
92 .into();
93 };
94
95 let analysis = match parse::analyze(&module, items, ®istered_events) {
96 Ok(analysis) => analysis,
97 Err(e) => return e.to_compile_error().into(),
98 };
99
100 generate::contract_module(&module, items, &analysis).into()
101}