ruex_macro/
lib.rs

1// #![feature(proc_macro_span)]
2#![allow(unused_imports)]
3#![allow(clippy::needless_doctest_main)]
4// #![warn(
5//     missing_debug_implementations,
6//     missing_docs,
7//     rust_2018_idioms,
8//     unreachable_pub
9// )]
10#![doc(test(
11    no_crate_inject,
12    attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
13))]
14
15use proc_macro::{Span, TokenStream, TokenTree};
16
17mod impls;
18use impls::{ItemLocation, Mode, Type};
19
20const MAX_UDP_PAYLOAD: usize = 65507;
21
22/// Composition root for application entry point.
23///
24/// Inspired by `#[tokio::main]`
25///
26/// Implements dependency injection composition root pattern for
27/// application entry point.
28///
29/// ## Examples
30///
31/// ```
32/// #[ruex::main {
33///     backtrace = {
34///         level = Short,
35///     },
36///     log = {
37///         level = Info,
38///         output = Stdout,
39///     }
40/// }]
41/// fn main() {}
42/// ```
43///
44/// ## Config
45/// Configuration fields can be omitted.
46///
47/// ```rust,no_run
48/// struct MainConfig {
49///     backtrace: BacktraceConfig,
50///     log: LogConfig,
51/// }
52///
53/// struct LogConfig {
54///     level: LogLevel,
55///     output: LogOutput,
56/// }
57///
58/// enum LogOutput {
59///     #[default]
60///     StdOut,
61///     Syslog,
62///     File(String),
63/// }
64///
65/// enum LogLevel {
66///     Trace,
67///     Debug,
68///     #[default]
69///     Info,
70///     Warn,
71///     Error,
72/// }
73///
74/// struct BacktraceConfig {
75///     level: BactraceLevel,
76/// }
77///
78/// enum BactraceLevel {
79///     None,
80///     #[default]
81///     Short,
82///     Full,
83/// }
84/// ```
85#[proc_macro_attribute]
86#[cfg(not(test))] // Work around for rust-lang/rust#62127
87pub fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
88    impls::main(attr, item)
89}
90
91/// Decorate methods.
92///
93/// Inspired by `deco`
94///
95/// ## Examples
96///
97/// ```
98/// use ruex::prelude::*;
99///
100/// fn logging<F>(func: F) -> impl Fn(i32) -> i32
101/// where
102///     F: Fn(i32) -> i32,
103/// {
104///     move |i| {
105///         println!("Input = {}", i);
106///         let out = func(i);
107///         println!("Output = {}", out);
108///         out
109///     }
110/// }
111///
112/// #[decorate(logging)]
113/// fn add2(i: i32) -> i32 {
114///     i + 2
115/// }
116///
117/// add2(2);
118/// ```
119///
120/// - Decorator with parameter
121///
122/// ```
123/// use ruex::prelude::*;
124/// use std::{fs, io::Write};
125///
126/// fn logging<InputFunc: 'static>(
127///     log_filename: &'static str,
128/// ) -> impl Fn(InputFunc) -> Box<dyn Fn(i32) -> i32>
129/// where
130///     InputFunc: Fn(i32) -> i32,
131/// {
132///     move |func: InputFunc| {
133///         Box::new(move |i: i32| {
134///             let mut f = fs::File::create(log_filename).unwrap();
135///             writeln!(f, "Input = {}", i).unwrap();
136///             let out = func(i);
137///             writeln!(f, "Output = {}", out).unwrap();
138///             out
139///         })
140///     }
141/// }
142///
143/// #[decorate(logging("test.log"))]
144/// fn add2(i: i32) -> i32 {
145///     i + 2
146/// }
147///
148/// add2(2);
149/// ```
150///
151#[proc_macro_attribute]
152pub fn decorate(attr: TokenStream, item: TokenStream) -> TokenStream {
153    impls::decorate(attr, item)
154}
155
156/// Delegate method to a field.
157///
158/// Inspired by delegate-attr
159///
160/// ## Examples
161///
162/// ### Delegate `impl` block
163///
164/// ```
165/// use delegate_attr::delegate;
166///
167/// struct Foo(String);
168///
169/// #[delegate(self.0)]
170/// impl Foo {
171///     fn as_str(&self) -> &str;
172///     fn into_bytes(self) -> Vec<u8>;
173/// }
174///
175/// let foo = Foo("hello".to_owned());
176/// assert_eq!(foo.as_str(), "hello");
177/// assert_eq!(foo.into_bytes(), b"hello");
178/// ```
179///
180/// ### Delegate trait `impl`
181///
182/// ```
183/// # use delegate_attr::delegate;
184///
185/// struct Iter(std::vec::IntoIter<u8>);
186///
187/// #[delegate(self.0)]
188/// impl Iterator for Iter {
189///     type Item = u8;
190///     fn next(&mut self) -> Option<u8>;
191///     fn count(self) -> usize;
192///     fn size_hint(&self) -> (usize, Option<usize>);
193///     fn last(self) -> Option<u8>;
194/// }
195///
196/// let iter = Iter(vec![1, 2, 4, 8].into_iter());
197/// assert_eq!(iter.count(), 4);
198/// let iter = Iter(vec![1, 2, 4, 8].into_iter());
199/// assert_eq!(iter.last(), Some(8));
200/// let iter = Iter(vec![1, 2, 4, 8].into_iter());
201/// assert_eq!(iter.sum::<u8>(), 15);
202/// ```
203///
204/// ### With more complicated target
205///
206/// ```
207/// # use delegate_attr::delegate;
208/// # use std::cell::RefCell;
209/// struct Foo<T> {
210///     inner: RefCell<Vec<T>>,
211/// }
212///
213/// #[delegate(self.inner.borrow())]
214/// impl<T> Foo<T> {
215///     fn len(&self) -> usize;
216/// }
217///
218/// #[delegate(self.inner.borrow_mut())]
219/// impl<T> Foo<T> {
220///     fn push(&self, value: T);
221/// }
222///
223/// #[delegate(self.inner.into_inner())]
224/// impl<T> Foo<T> {
225///     fn into_boxed_slice(self) -> Box<[T]>;
226/// }
227///
228/// let foo = Foo { inner: RefCell::new(vec![1]) };
229/// assert_eq!(foo.len(), 1);
230/// foo.push(2);
231/// assert_eq!(foo.len(), 2);
232/// assert_eq!(foo.into_boxed_slice().as_ref(), &[1, 2]);
233/// ```
234///
235/// ### `into` and `call` attribute
236///
237/// ```
238/// # use delegate_attr::delegate;
239/// struct Inner;
240/// impl Inner {
241///     pub fn method(&self, num: u32) -> u32 { num }
242/// }
243///
244/// struct Wrapper { inner: Inner }
245///
246/// #[delegate(self.inner)]
247/// impl Wrapper {
248///     // calls method, converts result to u64
249///     #[into]
250///     pub fn method(&self, num: u32) -> u64;
251///
252///     // calls method, returns ()
253///     #[call(method)]
254///     pub fn method_noreturn(&self, num: u32);
255/// }
256/// ```
257///
258/// ### Delegate single method
259///
260/// ```
261/// # use delegate_attr::delegate;
262/// struct Foo<T>(Vec<T>);
263///
264/// impl<T> Foo<T> {
265///     #[delegate(self.0)]
266///     fn len(&self) -> usize;
267/// }
268///
269/// let foo = Foo(vec![1]);
270/// assert_eq!(foo.len(), 1);
271/// ```
272#[proc_macro_attribute]
273pub fn delegate(attr: TokenStream, item: TokenStream) -> TokenStream {
274    impls::delegate(attr, item)
275}
276
277/// Elegant trait mocking.
278///
279/// Use it with #[cfg_attr(test, mock)]
280///
281/// ## Examples
282///
283/// ```rust
284/// #![allow(unused_variables)]
285/// use ruex::prelude::*;
286///
287/// /// Here
288/// #[mock]
289/// trait Nurse {
290///     fn heal(&self, value: i32, direction: i32) -> i32 {
291///         0
292///     }
293///
294///     fn leave(&self, value: i32) -> i32 {
295///         0
296///     }
297/// }
298///
299/// #[derive(Default)]
300/// struct Foo;
301///
302/// impl Nurse for Foo {
303///     fn heal(&self, value: i32, direction: i32) -> i32 {
304///         25
305///     }
306///
307///     fn leave(&self, value: i32) -> i32 {
308///         31
309///     }
310/// }
311///
312/// fn main() {
313///     let nurse = Foo::mock().heal(|value, direction| 123).build();
314///
315///     let val = nurse.heal(23, 0);
316///     println!("VALUE: {val}");
317///
318///     let val = nurse.leave(23);
319///     println!("VALUE: {val}");
320/// }
321/// ```
322#[proc_macro_attribute]
323pub fn mock(attr: TokenStream, item: TokenStream) -> TokenStream {
324    impls::mock(attr, item)
325}
326
327/// Subject registration.
328#[proc_macro_attribute]
329pub fn register(attr: TokenStream, item: TokenStream) -> TokenStream {
330    impls::register(attr.into(), item.into()).into()
331}
332
333/// Subject mounting.
334#[proc_macro_attribute]
335pub fn mount(attr: TokenStream, item: TokenStream) -> TokenStream {
336    // let mut it = item.clone().into_iter();
337    // let first = it.next().unwrap();
338    // let last = it.last().unwrap();
339
340    // let path = first
341    //         .span()
342    //         .source_file()
343    //         .path()
344    //         .to_str()
345    //         .unwrap()
346    //         .to_string();
347    // let range = (first.span().start().line, last.span().end().line);
348    let path = String::new();
349    let pos = ItemLocation {
350        path,
351        range: (0, 0),
352    };
353
354    impls::mount(attr.into(), item.into(), pos).into()
355}
356
357/// Aspect-oriented methodology.
358///
359#[proc_macro_attribute]
360pub fn aspect(attr: TokenStream, item: TokenStream) -> TokenStream {
361    impls::contracts_aspect(Type::Aspect, Mode::Always, attr.into(), item.into()).into()
362}
363
364/// Composition pattern implementation.
365///
366#[proc_macro_derive(Compose, attributes(delegate))]
367pub fn derive_compose(item: TokenStream) -> TokenStream {
368    impls::compose(item.into()).into()
369}
370
371/// Debug ensures for contracts.
372///
373#[proc_macro_attribute]
374pub fn debug_ensures(attr: TokenStream, item: TokenStream) -> TokenStream {
375    impls::contracts_aspect(Type::Ensures, Mode::Debug, attr.into(), item.into()).into()
376}
377
378/// Debug invariant for contracts.
379///
380#[proc_macro_attribute]
381pub fn debug_invariant(attr: TokenStream, item: TokenStream) -> TokenStream {
382    impls::contracts_aspect(Type::Invariant, Mode::Debug, attr.into(), item.into()).into()
383}
384
385/// Debug requires for contracts.
386///
387#[proc_macro_attribute]
388pub fn debug_requires(attr: TokenStream, item: TokenStream) -> TokenStream {
389    impls::contracts_aspect(Type::Requires, Mode::Debug, attr.into(), item.into()).into()
390}
391
392/// Ensures for contracts.
393///
394#[proc_macro_attribute]
395pub fn ensures(attr: TokenStream, item: TokenStream) -> TokenStream {
396    impls::contracts_aspect(Type::Ensures, Mode::Always, attr.into(), item.into()).into()
397}
398
399/// Invariant for contracts.
400///
401#[proc_macro_attribute]
402pub fn invariant(attr: TokenStream, item: TokenStream) -> TokenStream {
403    impls::contracts_aspect(Type::Invariant, Mode::Always, attr.into(), item.into()).into()
404}
405
406/// Requires for contracts.
407///
408#[proc_macro_attribute]
409pub fn requires(attr: TokenStream, item: TokenStream) -> TokenStream {
410    impls::contracts_aspect(Type::Requires, Mode::Always, attr.into(), item.into()).into()
411}
412
413/// Test ensures for contracts.
414///
415#[proc_macro_attribute]
416pub fn test_ensures(attr: TokenStream, item: TokenStream) -> TokenStream {
417    impls::contracts_aspect(Type::Ensures, Mode::Test, attr.into(), item.into()).into()
418}
419
420/// Test invariant for contracts.
421///
422#[proc_macro_attribute]
423pub fn test_invariant(attr: TokenStream, item: TokenStream) -> TokenStream {
424    impls::contracts_aspect(Type::Invariant, Mode::Test, attr.into(), item.into()).into()
425}
426
427/// Test requires for contracts.
428///
429#[proc_macro_attribute]
430pub fn test_requires(attr: TokenStream, item: TokenStream) -> TokenStream {
431    impls::contracts_aspect(Type::Requires, Mode::Test, attr.into(), item.into()).into()
432}