Skip to main content

stub_macro/
lib.rs

1//! `stub!()` can be assigned to a variable:
2//!
3//! ```rust
4//! # #[macro_use] extern crate stub_macro;
5//! # fn main() {
6//! # fn assign() {
7//! let username = stub!(String);
8//! # }
9//! # }
10//! ```
11//!
12//! This allows you to specify just type of the variable and continue working on other code, then come back later and specify its value.
13//!
14//! ## Examples
15//!
16//! ```
17//! # #[macro_use] extern crate stub_macro;
18//! # #[cfg(feature = "futures")]
19//! # extern crate futures_core_0_3 as futures_core;
20//! # fn main() {
21//! fn assign() {
22//!     // you can assign stub!() to a variable
23//!     let username = stub!(String);
24//!     println!("Hello {username}")
25//! }
26//!
27//! fn return_position() -> String {
28//!     // you can use stub!() like todo!() in return position
29//!     stub!()
30//! }
31//!
32//! fn infer_type() {
33//!     // you can let the compiler automatically infer the type
34//!     let status = stub!();
35//!     if status { println!("Success") }
36//! }
37//!
38//! fn explicit_type() {
39//!     // you can specify the type explicitly
40//!     let status: bool = stub!();
41//!     if status { println!("Success") }
42//! }
43//!
44//! fn custom_message() {
45//!     // you can add a custom message
46//!     let status: bool = stub!("Send a request to GitHub");
47//!     if status { println!("Success") }
48//! }
49//!
50//! # #[cfg(feature = "alloc")]
51//! fn impl_example() -> impl core::fmt::Display {
52//!     // you can use stub!() in return position even with `impl Trait` return type
53//!     // note: `impl Trait` must be written as `impl dyn Trait` due to `macro_rules!` limitation
54//!     stub!(impl dyn core::fmt::Display)
55//! }
56//!
57//! fn iter_example() -> impl Iterator<Item = u32> {
58//!     // use stub_iter!() when the return type is an iterator
59//!     stub_iter!()
60//! }
61//!
62//! #[cfg(feature = "futures")]
63//! fn stream_example() -> impl futures_core::stream::Stream<Item = u32> {
64//!     // use stub_stream!() when the return type is a stream
65//!     stub_stream!()
66//! }
67//!
68//! fn explicit_type_with_message_example() -> u32 {
69//!     // you can add
70//!     stub!(u32, "Assigned to: {}", "John")
71//! }
72//!
73//! fn explicit_type_example() -> u32 {
74//!     stub!(u32)
75//! }
76//!
77//! fn implicit_type_with_message_example() -> u32 {
78//!     stub!("Assigned to: {}", "John")
79//! }
80//!
81//! fn implicit_type_example() -> u32 {
82//!     stub!()
83//! }
84//! # }
85//! ```
86//!
87//! ## Behavior
88//!
89//! When a stub is invoked, it will panic like a `todo!()` macro.
90//! However, unlike a `todo!()` macro, it will not make the subsequent parts of your code unreachable.
91//!
92//! If a custom message is provided, it will be included in the panic message.
93//!
94//! ## Notes
95//!
96//! - `stub!()` macro is intended for use during development and should be replaced with actual implementations before production use.
97//! - When using `impl Trait` in return position, you must use `impl dyn Trait` in the macro invocation due to a limitation in `macro_rules!`
98//! - `stub!(impl dyn Trait)` requires the `alloc` feature.
99//! - `stub_stream!()` requires the `futures` feature.
100//!
101
102#![no_std]
103
104#[cfg(feature = "alloc")]
105extern crate alloc;
106
107#[doc(hidden)]
108#[macro_export]
109#[cfg(feature = "alloc")]
110macro_rules! _stub_impl_dyn {
111    ($ty:ty) => {{
112        $crate::_stub_box::<$ty>()
113    }};
114}
115
116#[doc(hidden)]
117#[macro_export]
118#[cfg(not(feature = "alloc"))]
119macro_rules! _stub_impl_dyn {
120    ($ty:ty) => {{
121        compile_error!("stub!(impl dyn ...) requires the `alloc` feature")
122    }};
123}
124
125/// See the crate-level documentation for the overview of this macro
126#[macro_export]
127macro_rules! stub {
128    (impl $ty:ty) => {{
129        $crate::_stub_impl_dyn!($ty)
130    }};
131    ($ty:ty, $fmt:expr) => {{
132        $crate::_stub_msg::<$ty>(format_args!($fmt))
133    }};
134    ($ty:ty, $fmt:expr, $($args:tt)*) => {{
135        $crate::_stub_msg::<$ty>(format_args!($fmt, $($args)*))
136    }};
137    ($ty:ty) => {{
138        $crate::_stub::<$ty>()
139    }};
140    ($fmt:expr) => {{
141        $crate::_stub_msg(format_args!($fmt))
142    }};
143    ($fmt:expr, $($args:tt)*) => {{
144        $crate::_stub_msg(format_args!($fmt, $($args)*))
145    }};
146    () => {{
147        $crate::_stub()
148    }};
149}
150
151/// See the crate-level documentation for the overview of this macro
152#[macro_export]
153macro_rules! stub_iter {
154    () => {
155        $crate::_stub::<core::iter::Empty<_>>()
156    };
157    ($fmt:expr) => {
158        $crate::_stub_msg::<core::iter::Empty<_>>(format_args!($fmt))
159    };
160    ($fmt:expr, $($args:tt)*) => {
161        $crate::_stub_msg::<core::iter::Empty<_>>(format_args!($fmt, $($args)*))
162    };
163}
164
165/// See the crate-level documentation for the overview of this macro
166#[macro_export]
167#[cfg(feature = "futures")]
168macro_rules! stub_stream {
169    () => {
170        $crate::_stub::<$crate::_StubEmptyStream<_>>()
171    };
172    ($fmt:expr) => {
173        $crate::_stub_msg::<$crate::_StubEmptyStream<_>>(format_args!($fmt))
174    };
175    ($fmt:expr, $($args:tt)*) => {
176        $crate::_stub_msg::<$crate::_StubEmptyStream<_>>(format_args!($fmt, $($args)*))
177    };
178}
179
180#[doc(hidden)]
181#[cfg(feature = "futures")]
182pub use futures_util_0_3::stream::Empty as _StubEmptyStream;
183
184#[doc(hidden)]
185pub fn _stub<T>() -> T {
186    todo!()
187}
188
189#[doc(hidden)]
190#[cfg(feature = "alloc")]
191pub fn _stub_box<T: ?Sized>() -> alloc::boxed::Box<T> {
192    _stub::<alloc::boxed::Box<T>>()
193}
194
195#[doc(hidden)]
196pub fn _stub_msg<T>(msg: core::fmt::Arguments<'_>) -> T {
197    todo!("{msg}")
198}
199
200#[cfg(test)]
201mod tests {
202    #[test]
203    #[should_panic]
204    fn test_panic() {
205        stub!()
206    }
207
208    #[test]
209    #[should_panic(expected = "Assigned to: John")]
210    fn test_panic_msg() {
211        let _: u32 = stub!(u32, "Assigned to: {}", "John");
212    }
213
214    #[cfg(feature = "alloc")]
215    #[test]
216    #[should_panic(expected = "Assigned to: x")]
217    fn test_panic_msg_with_path_expression() {
218        let _: u32 = stub!(u32, "Assigned to: {}", alloc::string::String::from("x"));
219    }
220
221    #[test]
222    #[should_panic(expected = "Assigned to: John")]
223    fn test_panic_msg_with_format_expression() {
224        let _: u32 = stub!(u32, concat!("Assigned to: ", "{}"), "John");
225    }
226
227    #[test]
228    #[should_panic(expected = "Assigned to: John")]
229    fn test_panic_msg_with_named_argument() {
230        let name = "John";
231        let _: u32 = stub!(u32, "Assigned to: {name}", name = name);
232    }
233
234    #[test]
235    #[should_panic]
236    fn test_panic_iter() {
237        fn iter() -> impl Iterator<Item = u32> {
238            stub_iter!()
239        }
240        let _ = iter();
241    }
242
243    #[cfg(feature = "alloc")]
244    #[test]
245    #[should_panic(expected = "Assigned to: x")]
246    fn test_panic_iter_with_path_expression() {
247        fn iter() -> impl Iterator<Item = u32> {
248            stub_iter!("Assigned to: {}", alloc::string::String::from("x"))
249        }
250        let _ = iter();
251    }
252
253    #[test]
254    #[should_panic(expected = "Assigned to: John")]
255    fn test_panic_iter_with_format_expression() {
256        fn iter() -> impl Iterator<Item = u32> {
257            stub_iter!(concat!("Assigned to: ", "{}"), "John")
258        }
259        let _ = iter();
260    }
261
262    #[test]
263    #[should_panic(expected = "Assigned to: John")]
264    fn test_panic_iter_with_named_argument() {
265        fn iter() -> impl Iterator<Item = u32> {
266            let name = "John";
267            stub_iter!("Assigned to: {name}", name = name)
268        }
269        let _ = iter();
270    }
271
272    #[cfg(feature = "alloc")]
273    #[test]
274    #[should_panic]
275    fn test_panic_dyn_display() {
276        fn display() -> impl core::fmt::Display {
277            stub!(impl dyn core::fmt::Display)
278        }
279        let _ = display();
280    }
281
282    #[cfg(feature = "alloc")]
283    #[test]
284    #[should_panic]
285    fn test_panic_dyn_iter() {
286        fn iter() -> impl Iterator<Item = u32> {
287            stub!(impl dyn Iterator<Item = u32>)
288        }
289        let _ = iter();
290    }
291
292    #[cfg(feature = "futures")]
293    #[test]
294    #[should_panic]
295    fn test_panic_stream() {
296        fn stream() -> impl futures_core_0_3::stream::Stream<Item = u32> {
297            stub_stream!()
298        }
299        let _ = stream();
300    }
301
302    #[cfg(all(feature = "alloc", feature = "futures"))]
303    #[test]
304    #[should_panic(expected = "Assigned to: x")]
305    fn test_panic_stream_with_path_expression() {
306        fn stream() -> impl futures_core_0_3::stream::Stream<Item = u32> {
307            stub_stream!("Assigned to: {}", alloc::string::String::from("x"))
308        }
309        let _ = stream();
310    }
311
312    #[cfg(feature = "futures")]
313    #[test]
314    #[should_panic(expected = "Assigned to: John")]
315    fn test_panic_stream_with_format_expression() {
316        fn stream() -> impl futures_core_0_3::stream::Stream<Item = u32> {
317            stub_stream!(concat!("Assigned to: ", "{}"), "John")
318        }
319        let _ = stream();
320    }
321
322    #[cfg(feature = "futures")]
323    #[test]
324    #[should_panic(expected = "Assigned to: John")]
325    fn test_panic_stream_with_named_argument() {
326        fn stream() -> impl futures_core_0_3::stream::Stream<Item = u32> {
327            let name = "John";
328            stub_stream!("Assigned to: {name}", name = name)
329        }
330        let _ = stream();
331    }
332}