random_branch/
lib.rs

1#![no_std]
2
3// Enable annotating features requirements in docs
4#![cfg_attr(feature = "doc_cfg", feature(doc_cfg))]
5
6// This crate is entirely safe, actually it's just macros
7#![forbid(unsafe_code)]
8
9// Ensures that `pub` means published in the public API.
10// This property is useful for reasoning about breaking API changes.
11#![deny(unreachable_pub)]
12
13// Denies invalid links in docs
14#![deny(rustdoc::broken_intra_doc_links)]
15
16//! Provides a macro to select a random branch.
17//!
18//! This crate provides the [`branch`](crate::branch) and
19//! [`branch_using`](crate::branch_using) macro, which will
20//! execute randomly one of the given expressions.
21//!
22//! It is maybe best visualized by the following example:
23//!
24//! ```rust
25//! # #[cfg(features = "std")] { // only with std
26//! # use random_branch::branch;
27//! branch!(
28//!     println!("First line."),
29//!     println!("Second line?"),
30//!     println!("Third line!"),
31//! );
32//! # } // only with std
33//! ```
34//!
35//! This will be turned into something similar to this:
36//!
37//! ```rust
38//! # #[cfg(features = "std")] { // only with std
39//! # use rand::Rng;
40//! match rand::rng().random_range(0..3) {
41//!     0 => println!("First line."),
42//!     1 => println!("Second line?"),
43//!     2 => println!("Third line!"),
44//!     _ => unreachable!(),
45//! }
46//! # } // only with std
47//! ```
48//!
49//! For more details see [`branch`](crate::branch) and
50//! [`branch_using`](crate::branch_using). The basic difference between them is,
51//! that `branch` uses [`rand::rng()`](rand::rng()) whereas
52//! `branch_using` uses the the given [`rand::Rng`](rand::Rng).
53//!
54
55
56// Reexport our version of rand so we can use it from our macros.
57#[doc(hidden)]
58pub use rand;
59
60
61/// Branches into one of the given expressions using the given RNG.
62///
63/// This macro dose essentially the same as [`branch`] but uses the given
64/// [`Rng`](rand::Rng).
65///
66/// This macro turns something like this:
67///
68/// ```rust
69/// # use rand_pcg::Lcg64Xsh32;
70/// # use random_branch::branch_using;
71/// let mut my_rng = /* snip */
72/// # Lcg64Xsh32::new(0,0);
73///
74/// branch_using!( my_rng, {
75///     println!("First line."),
76///     println!("Second line?"),
77///     println!("Third line!"),
78/// });
79/// ```
80///
81/// into something similar to this:
82///
83/// ```rust
84/// # use rand_pcg::Lcg64Xsh32;
85/// let mut my_rng = /* snip */
86/// # Lcg64Xsh32::new(0,0);
87/// # use rand::Rng;
88///
89/// match my_rng.random_range(0..3) {
90///     0 => println!("First line."),
91///     1 => println!("Second line?"),
92///     2 => println!("Third line!"),
93///     _ => unreachable!(),
94/// }
95/// ```
96///
97/// # Examples
98///
99/// You can use functions, macros and other arbitrary expressions:
100///
101/// ```rust
102/// # use rand_pcg::Lcg64Xsh32;
103/// use random_branch::branch_using;
104/// fn do_something() {
105///      println!("There is no such thing")
106/// }
107/// let thing = "fuliluf";
108/// let mut my_rng = /* snip */
109/// # Lcg64Xsh32::new(0,0);
110///
111/// branch_using!( my_rng, {
112///     println!("A {} is an animal!", thing),
113///     {
114///         let thing = "lufiful";
115///         println!("Two {}s will never meet.", thing)
116///     },
117///     println!("Only a {} can see other {0}s.", thing),
118///     do_something(),
119/// });
120/// ```
121///
122/// You can also use it as an expression to yield some randomly chosen value:
123///
124/// ```rust
125/// # use rand_pcg::Lcg64Xsh32;
126/// use random_branch::branch_using;
127/// let mut my_rng = /* snip */
128/// # Lcg64Xsh32::new(0,0);
129///
130/// let num = branch_using!( my_rng, {
131///     10,
132///     10 + 11,
133///     2 * (10 + 11),
134///     85,
135/// });
136/// assert!(num == 10 || num == 21 || num == 42 || num == 85);
137/// ```
138#[macro_export]
139macro_rules! branch_using {
140	( $rng:expr, { $( $branch:expr ),* $(,)? }) => {
141		{
142			$crate::branch_internal!(
143				$rng,
144				{ $( { $branch } )* },
145			)
146		}
147	};
148}
149
150
151/// Branches into one of the given expressions.
152///
153/// This macro dose essentially the same as [`branch_using`] instead of giving
154/// it some RNG, this macro will simply use the [`rand::rng()`].
155/// However, this then requires `std`, unlike `branch_using`.
156///
157/// This macro turns something like this:
158///
159/// ```rust
160/// # use random_branch::branch;
161/// branch!(
162///     println!("First line."),
163///     println!("Second line?"),
164///     println!("Third line!"),
165/// );
166/// ```
167///
168/// into something similar to this using the `rand::rng()`:
169///
170/// ```rust
171/// # use rand::Rng;
172/// match rand::rng().random_range(0..3) {
173///     0 => println!("First line."),
174///     1 => println!("Second line?"),
175///     2 => println!("Third line!"),
176///     _ => unreachable!(),
177/// }
178/// ```
179///
180///
181/// # Examples
182///
183/// You can use functions, macros and other arbitrary expressions:
184///
185/// ```rust
186/// use random_branch::branch;
187///
188/// fn do_something() {
189///      println!("There is no such thing")
190/// }
191/// let thing = "fuliluf";
192///
193/// branch!(
194///     println!("A {} is an animal!", thing),
195///     {
196///         let thing = "lufiful";
197///         println!("Two {}s will never meet.", thing)
198///     },
199///     println!("Only a {} can see other {0}s.", thing),
200///     do_something(),
201/// );
202/// ```
203///
204/// You can also use it as an expression to yield some randomly chosen value:
205///
206/// ```rust
207/// use random_branch::branch;
208///
209/// let num = branch!(
210///     10,
211///     10 + 11,
212///     2 * (10 + 11),
213///     85,
214/// );
215/// println!("The best number is {}", num);
216/// # assert!(num == 10 || num == 21 || num == 42 || num == 85);
217/// ```
218#[macro_export]
219#[cfg(feature = "std")]
220#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "std")))]
221macro_rules! branch {
222	( $( $branch:expr ),* $(,)? ) => {
223		{
224			$crate::branch_internal!(
225				$crate::rand::rng(),
226				{ $( { $branch } )* },
227			)
228		}
229	};
230}
231
232
233/// Internal branching macro
234///
235/// Each branch must be enclosed in braces e.g. `{ }` so it is a single `tt`.
236///
237/// Syntax:
238/// ```text
239/// branch_internal!([RNG], [BRANCHES]+)
240/// ```
241#[doc(hidden)]
242#[macro_export]
243macro_rules! branch_internal {
244	// Entry pattern
245	( $rng:expr, { $( $branches:tt )* }, ) => {
246		$crate::branch_internal!(@parseRule $rng, 0, {}, { $( $branches )* },)
247	};
248
249	// Invalid, base case
250	(@parseRule $rng:expr, $cnt:expr,
251		{  },
252		{  },
253	) => {
254		compile_error!("You must provide at least one choice.")
255	};
256	// Prepares one branch at a time
257	(@parseRule $rng:expr, $cnt:expr,
258		{ $( $stuff:tt )* },
259		{ $branch:tt $( $rest:tt )* },
260	) => {
261		{
262			$crate::branch_internal!(@parseRule $rng, $cnt + 1,
263				{ $( $stuff )* { $cnt => $branch } },
264				{ $( $rest )* },
265			)
266		}
267	};
268	// Assembles all branches into a big match
269	(@parseRule $rng:expr, $cnt:expr,
270		{ $( { $cc:expr => $branch:tt } )* },
271		{ },
272	) => {{
273		match $crate::rand::Rng::random_range(&mut $rng, 0 .. ($cnt)) {
274			$( n if n == $cc => $branch )*
275			_ => unreachable!()
276		}
277	}};
278}
279
280#[cfg(test)]
281mod tests {
282    // We actually use mostly doc-tests, which are better suited for macro tests
283}