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}