File: modules.rs

package info (click to toggle)
rust-derive-deftly 1.6.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,652 kB
  • sloc: perl: 1,032; sh: 373; python: 227; makefile: 11
file content (140 lines) | stat: -rw-r--r-- 5,215 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! Test/demonstrate within-crate cross-module scoping
//!
//! In practice, one would probably put the "ought to be in scope"
//! names into an internal prelude module, and `use internal_prelude::*`.
//! That would allow natural use of the identifiers from the prelude
//! everywhere and avoid having to write qualified paths in templates
//! or worry about precisely which things should be imported where.
//!
//! Sadly `macro_rules!` macro scoping doesn't work that way, so one
//! would still need the `#[macro_use]` annotations.  And, the ordering
//! of the code is significant - so it can be necessary to have a particular
//! ordering of `mod` statements.

// Reminder, this is a *module* inside a test crate.
// The name of this module is `crate::modules`
// and it is within `#[cfg(test)]`.

#![allow(dead_code)]

struct EnumMarker;
trait IsEnum {
    fn is_enum() -> Option<EnumMarker>;
}
trait NumFields {
    fn num_fields() -> usize;
}

/// When defining a template that is to be used outside its defining
/// module, but inside the same crate, `#[macro_use]` is needed, on the
/// containing module(s).  The template is then available *textually after*
/// its definition.
///
/// (Exporting of a template to other crates is demonstrated in
/// `tests/pub-export/pub-b/pub-a.rs` and `pub-b.rs`.)
///
/// The remaining principles about use of names in a derive-deftly template
/// are basically the same as those for a `macro_rules!` macro.
#[macro_use]
mod has_template {
    use derive_deftly::define_derive_deftly;

    /// Demonstrates use of a local name in a template.
    /// We must make it visible everywhere the template will be expanded.
    pub(super) const ENUM_MARKER: Option<super::EnumMarker> =
        Some(super::EnumMarker);

    define_derive_deftly! {
        IsEnum:

        // When the template wants to refer to things, it is usually most
        // convenient to expect the template user to bring the needed parts
        // into scope at the template invocation site.  We do that here
        // for IsEnum and EnumMarker;
        impl<$tgens> IsEnum for $ttype {
            fn is_enum() -> Option<EnumMarker> {
                // Alternatively, the template can refer to items by
                // absolute crate path.  The items must still be visible
                // at the template invocation site.
                //
                // We do that here with `ENUM_MARKER`.
                //
                // (`super::` isn't adviseable in a macro, including in a
                // derive-deftly template, because its meaning at the
                // expansion site would depend on the scope context there
                // (eg, module depth.)
                ${if is_enum {
                    crate::modules::has_template::ENUM_MARKER
                } else {
                    None
                } }
            }
        }
    }
}

/// When definining a driver that is to be used as a derive input
/// outside its defining module, but inside the same crate,
/// `#[macro_use]` is needed, on the containing module(s).  The driver
/// is then available for `derive_deftly_adhoc!` *textually after* its
/// definition.
#[macro_use]
pub mod has_driver {
    use derive_deftly::Deftly;

    // Additionally, for a driver to be useable outside its module, the
    // driver type name and types of its fields etc., need to be visible,
    // for the use of expansions in other modules.
    pub struct Field<T: Default>(T);

    // When applying a template that has expectations about the
    // invocation scope, we need to satisfy those, by bringing into
    // scope the things that the template expects.
    use super::{EnumMarker, IsEnum};

    #[derive(Deftly)]
    #[derive_deftly(IsEnum)]
    #[derive_deftly_adhoc]
    pub enum Driver<T: Default> {
        Variant(Field<T>),
    }
}

mod adhoc_template {
    use derive_deftly::derive_deftly_adhoc;

    // When applying a template to a driver struct defined in another
    // module, the driver's own type name, and the names used for field
    // type=s etc.  that the macro might muse, must be brought into scope
    // manually.
    use super::has_driver::{Driver, Field};

    derive_deftly_adhoc! {
        // When expanding a template for a crate-local struct, pass
        // just the name of the struct to `derive_deftly_adhoc!`.
        //
        // (Exporting of the driver so that other crates can derive from it
        // is supported via `#[derive_deftly(pub)]` but it brings
        // namespacing awkwardness and should be used only with crate.)
        //
        // (Passing a path doesn't work for a struct with an unexported
        // derive-deftly driver, because the name here is turned into
        // the name of a `macro_rules!` macro, and a crate-local one
        // of those those doesn't have any path scope.)
        Driver:

        impl<$tgens> super::NumFields for $ttype {
            fn num_fields() -> usize {
                $( let _: $ftype; )

                0 + ${for fields { 1 }}
            }
        }
    }
}

#[test]
fn invoke() {
    assert!(has_driver::Driver::<()>::is_enum().is_some());
    assert_eq!(has_driver::Driver::<()>::num_fields(), 1);
}