Skip to main content

binrw/
named_args.rs

1//! Types and macros for generating named arguments builders.
2
3use core::marker::PhantomData;
4
5/// A convenience macro for constructing
6/// [named arguments](crate::docs::attribute#named-arguments).
7///
8/// This macro uses the [`builder()`](NamedArgs::builder) function of a
9/// [named arguments type](NamedArgs), and can only be used in positions
10/// where the type can be inferred by the compiler (i.e. as a function argument
11/// or an assignment to a variable with an explicit type).
12///
13/// # Examples
14///
15/// ```
16/// use binrw::BinRead;
17/// # use binrw::io::Cursor;
18///
19/// #[derive(BinRead)]
20/// #[br(import { a: i32, b: i32 })]
21/// struct Foo;
22///
23/// let mut reader = Cursor::new(b"");
24/// let a = 1;
25/// Foo::read_args(&mut reader, binrw::args! {
26///     a,
27///     b: { a * 2 },
28/// }).unwrap();
29/// ```
30#[macro_export]
31macro_rules! args {
32    (@ifn { $value:expr } $name:ident) => { $value };
33    (@ifn {} $name:ident) => { $name };
34    ($($name:ident $(: $value:expr)?),* $(,)?) => {
35        {
36            // I'll use Ret to represent the type of the block
37            // token representing the type of the block. we request that the compiler infer it.
38            let args_ty = ::core::marker::PhantomData::<_>;
39            if false {
40                // this statement will never be run, but will be used for type resolution.
41                // since this helper is of PhantomData<T> -> T,
42                // and the compiler knows that the type Ret should be returned,
43                // it infers that args_ty should be of type PhantomData<Ret>.
44                $crate::__private::passthrough_helper(args_ty)
45            } else {
46                // we now pass the PhantomData<Ret> to a helper of PhantomData<T> -> T::Builder
47                // to obtain a builder for the type of the block.
48                let builder = $crate::__private::builder_helper(args_ty);
49
50                $(let builder = builder.$name($crate::args!(@ifn { $($value)? } $name));)*
51
52                // since the builder returns the type that we used to obtain the builder,
53                // the type unifies across the if expression
54                builder.finalize()
55            }
56        }
57    };
58}
59
60/// The `NamedArgs` trait allows
61/// [named arguments](crate::docs::attribute#named-arguments) objects
62/// to be constructed using a builder that checks for correctness at compile
63/// time.
64///
65/// See [`#[derive(NamedArgs)]`](derive@crate::NamedArgs) for information on deriving
66/// custom named arguments types.
67pub trait NamedArgs {
68    /// The builder type for this type.
69    type Builder;
70
71    /// Creates a new builder for this type.
72    fn builder() -> Self::Builder;
73}
74
75#[doc(hidden)]
76pub struct Satisfied;
77
78#[doc(hidden)]
79pub struct Optional;
80
81#[doc(hidden)]
82pub struct Needed;
83
84// TODO: seal?
85/// Indicates that a requirement for a typed builder has been met, either by
86/// the user providing one, or by a default being given.
87#[doc(hidden)]
88pub trait SatisfiedOrOptional {}
89
90impl SatisfiedOrOptional for Satisfied {}
91impl SatisfiedOrOptional for Optional {}
92
93#[doc(hidden)]
94#[cfg_attr(coverage_nightly, coverage(off))]
95#[must_use]
96pub fn passthrough_helper<T>(_a: PhantomData<T>) -> T {
97    panic!("This is a type system hack and should never be called!");
98}
99
100#[doc(hidden)]
101#[must_use]
102pub fn builder_helper<T: NamedArgs>(_: PhantomData<T>) -> T::Builder {
103    <T as NamedArgs>::builder()
104}