1
//! [`KeySpecifier`] derive-adhoc macro and its support code
2
//!
3
//! # STABILITY - NOTHING IN THIS MODULE IS PART OF THE STABLE PUBLIC API
4
//!
5
//! The `pub` items in this module are accessible as `$crate::key_specifier_derive`,
6
//! but `#[doc(hidden)]` is applied at the top level.
7
//!
8
//! (Recall that the actual derive-adhoc macro
9
//! `KeySpecifier` ends up in the crate toplevel,
10
//! so that *does* form part of our public API.)
11

            
12
use std::iter;
13

            
14
use derive_deftly::define_derive_deftly;
15
use itertools::{izip, EitherOrBoth, Itertools};
16

            
17
use super::*;
18
use crate::DENOTATOR_SEP;
19

            
20
pub use crate::KeyPathInfoBuilder;
21
pub use tor_error::{internal, into_internal, Bug};
22

            
23
/// Trait for (only) formatting as a [`KeySpecifierComponent`]
24
///
25
/// Like the formatting part of `KeySpecifierComponent`
26
/// but implemented for Option and &str too.
27
pub trait RawKeySpecifierComponent {
28
    /// Append `self`s `KeySpecifierComponent` string representation to `s`
29
    //
30
    // This is not quite like `KeySpecifierComponent::to_slug`,
31
    // since that *returns* a String (effectively) and we *append*.
32
    // At some future point we may change KeySpecifierComponent,
33
    // although the current API has the nice feature that
34
    // the syntax of the appended string is checked before we receive it here.
35
    fn append_to(&self, s: &mut String) -> Result<(), Bug>;
36
}
37
impl<T: KeySpecifierComponent> RawKeySpecifierComponent for T {
38
2206
    fn append_to(&self, s: &mut String) -> Result<(), Bug> {
39
2206
        self.to_slug()?.as_str().append_to(s)
40
2206
    }
41
}
42
impl<T: KeySpecifierComponent> RawKeySpecifierComponent for Option<T> {
43
88
    fn append_to(&self, s: &mut String) -> Result<(), Bug> {
44
88
        let v: &dyn RawKeySpecifierComponent = match self.as_ref() {
45
32
            Some(v) => v,
46
56
            None => &"*",
47
        };
48
88
        v.append_to(s)
49
88
    }
50
}
51
impl<'s> RawKeySpecifierComponent for &'s str {
52
73898
    fn append_to(&self, s: &mut String) -> Result<(), Bug> {
53
73898
        s.push_str(self);
54
73898
        Ok(())
55
73898
    }
56
}
57

            
58
/// Make a string like `pc/pc/pc/lc_lc_lc`
59
19644
fn arti_path_string_from_components(
60
19644
    path_comps: &[&dyn RawKeySpecifierComponent],
61
19644
    leaf_comps: &[&dyn RawKeySpecifierComponent],
62
19644
) -> Result<String, Bug> {
63
19644
    let mut path = String::new();
64

            
65
62480
    for comp in path_comps {
66
42836
        comp.append_to(&mut path)?;
67
42836
        path.push('/');
68
    }
69
31060
    for (delim, comp) in izip!(
70
19644
        iter::once(None).chain(iter::repeat(Some(DENOTATOR_SEP))),
71
19644
        leaf_comps,
72
19644
    ) {
73
31060
        if let Some(delim) = delim {
74
11416
            path.push(delim);
75
19644
        }
76
31060
        comp.append_to(&mut path)?;
77
    }
78

            
79
19644
    Ok(path)
80
19644
}
81

            
82
/// Make an `ArtiPath` like `pc/pc/pc/lc_lc_lc`
83
///
84
/// This is the engine for the `KeySpecifier` macro's `arti_path()` impls.
85
///
86
/// The macro-generated code sets up couple of vectors.
87
/// Each vector entry is a pointer to the field in the original struct,
88
/// plus a vtable pointer saying what to do with it.
89
///
90
/// For fixed elements in the path,
91
/// the vtable entry's data pointer is a pointer to a constant &str.
92
///
93
/// In the macro, this is done by the user-defined expansion `ARTI_FROM_COMPONENTS_ARGS`.
94
///
95
/// Doing it this way minimises the amount of macro-generated machine code.
96
19228
pub fn arti_path_from_components(
97
19228
    path_comps: &[&dyn RawKeySpecifierComponent],
98
19228
    leaf_comps: &[&dyn RawKeySpecifierComponent],
99
19228
) -> Result<ArtiPath, ArtiPathUnavailableError> {
100
19228
    Ok(arti_path_string_from_components(path_comps, leaf_comps)?
101
19228
        .try_into()
102
19228
        .map_err(into_internal!("bad ArtiPath from good components"))?)
103
19228
}
104

            
105
/// Make a `KeyPathPattern::Arti` like `pc/pc/pc/lc_lc_lc`
106
416
pub fn arti_pattern_from_components(
107
416
    path_comps: &[&dyn RawKeySpecifierComponent],
108
416
    leaf_comps: &[&dyn RawKeySpecifierComponent],
109
416
) -> Result<KeyPathPattern, Bug> {
110
416
    Ok(KeyPathPattern::Arti(arti_path_string_from_components(
111
416
        path_comps, leaf_comps,
112
416
    )?))
113
416
}
114

            
115
/// Error returned from [`RawKeySpecifierComponentParser::parse`]
116
#[derive(Debug)]
117
#[allow(clippy::exhaustive_enums)] // Not part of public API
118
pub enum RawComponentParseResult {
119
    /// This was a field
120
    ///
121
    /// The `Option` has been filled with the actual value.
122
    /// It has an entry in the `keys` argument to [`parse_key_path`].
123
    ParsedField,
124
    /// This was a literal, and it matched
125
    MatchedLiteral,
126
    /// Becomes [`KeyPathError::PatternNotMatched`]
127
    PatternNotMatched,
128
    /// `InvalidKeyPathComponentValue`
129
    Invalid(InvalidKeyPathComponentValue),
130
}
131

            
132
use RawComponentParseResult as RCPR;
133

            
134
/// Trait for parsing a path component, used by [`parse_key_path`]
135
///
136
/// Implemented for `Option<impl KeySpecifierComponent>`,
137
/// and guarantees to fill in the Option if it succeeds.
138
///
139
/// Also implemented for `&str`: just checks that the string is right,
140
/// (and, doesn't modify `*self`).
141
pub trait RawKeySpecifierComponentParser {
142
    /// Check that `comp` is as expected, and store any results in `self`.
143
    fn parse(&mut self, comp: &Slug) -> RawComponentParseResult;
144
}
145

            
146
impl<T: KeySpecifierComponent> RawKeySpecifierComponentParser for Option<T> {
147
600
    fn parse(&mut self, comp: &Slug) -> RawComponentParseResult {
148
600
        let v = match T::from_slug(comp) {
149
600
            Ok(v) => v,
150
            Err(e) => return RCPR::Invalid(e),
151
        };
152
600
        *self = Some(v);
153
600
        RCPR::ParsedField
154
600
    }
155
}
156
impl<'s> RawKeySpecifierComponentParser for &'s str {
157
6564
    fn parse(&mut self, comp: &Slug) -> RawComponentParseResult {
158
6564
        if comp.as_str() == *self {
159
6564
            RCPR::MatchedLiteral
160
        } else {
161
            RCPR::PatternNotMatched
162
        }
163
6564
    }
164
}
165

            
166
/// List of parsers for fields
167
type Parsers<'p> = [&'p mut dyn RawKeySpecifierComponentParser];
168

            
169
/// Parse a `KeyPath` as an `ArtiPath` like pc/pc/pc/lc_lc_lc
170
///
171
/// `keys` is the field names for each of the path_parsers and leaf_parsers,
172
/// *but* only the ones which will return `RCPR::ParsedField` (or `::Invalid`).
173
///
174
/// As with `arti_path_string_components` etc., we try to minimise
175
/// the amount of macro-generated machine code.
176
///
177
/// The macro-generated impl again assembles two vectors,
178
/// one for the path components and one for the leaf components.
179
///
180
/// For a field, the vector entry is a pointer to `&mut Option<...>`
181
/// for the field, along with a `RawKeySpecifierComponentParser` vtable entry.
182
/// (The macro-generated impl must unwrap each of these Options,
183
/// to assemble the final struct.  In principle this could be avoided with
184
/// use of `MaybeUninit` and unsafe.)
185
///
186
/// For a fixed string component, the vector entry data pointer points to its `&str`.
187
/// "Parsing" consists of checking that the string is as expected.
188
///
189
/// We also need the key names for error reporting.
190
/// We pass this as a *single* array, and a double-reference to the slice,
191
/// since that resolves to one pointer to a static structure.
192
3282
pub fn parse_key_path(
193
3282
    path: &KeyPath,
194
3282
    keys: &&[&str],
195
3282
    path_parsers: &mut Parsers,
196
3282
    leaf_parsers: &mut Parsers,
197
3282
) -> Result<(), KeyPathError> {
198
3282
    let (path, arti_path) = match path {
199
3282
        KeyPath::Arti(path) => (path.as_str(), path),
200
        KeyPath::CTor(_path) => {
201
            // TODO (#858): support ctor stores
202
            return Err(internal!("not implemented").into());
203
        }
204
    };
205

            
206
3282
    let (path, leaf) = match path.rsplit_once('/') {
207
3282
        Some((path, leaf)) => (Some(path), leaf),
208
        None => (None, path),
209
    };
210

            
211
3282
    let mut keys: &[&str] = keys;
212

            
213
    /// Split a string into components and parse each one
214
6564
    fn extract(
215
6564
        arti_path: &ArtiPath,
216
6564
        input: Option<&str>,
217
6564
        delim: char,
218
6564
        parsers: &mut Parsers,
219
6564
        keys: &mut &[&str],
220
6564
    ) -> Result<(), KeyPathError> {
221
15996
        for ent in Itertools::zip_longest(
222
6774
            input.map(|input| input.split(delim)).into_iter().flatten(),
223
6564
            parsers,
224
        ) {
225
15996
            let EitherOrBoth::Both(comp, parser) = ent else {
226
                // wrong number of components
227
                return Err(KeyPathError::PatternNotMatched(arti_path.clone()));
228
            };
229

            
230
            // TODO would be nice to avoid allocating again here,
231
            // but I think that needs an `SlugRef`.
232
15996
            let comp = Slug::new(comp.to_owned())
233
15996
                .map_err(ArtiPathSyntaxError::Slug)
234
15996
                .map_err(|error| KeyPathError::InvalidArtiPath {
235
                    error,
236
                    path: arti_path.clone(),
237
15996
                })?;
238

            
239
15996
            let missing_keys = || internal!("keys list too short, bad args to parse_key_path");
240

            
241
15996
            match parser.parse(&comp) {
242
                RCPR::PatternNotMatched => Err(KeyPathError::PatternNotMatched(arti_path.clone())),
243
                RCPR::Invalid(error) => Err(KeyPathError::InvalidKeyPathComponentValue {
244
                    error,
245
                    key: keys.first().ok_or_else(missing_keys)?.to_string(),
246
                    path: arti_path.clone(),
247
                    value: comp,
248
                }),
249
                RCPR::ParsedField => {
250
9432
                    *keys = keys.split_first().ok_or_else(missing_keys)?.1;
251
9432
                    Ok(())
252
                }
253
6564
                RCPR::MatchedLiteral => Ok(()),
254
            }?;
255
        }
256
6564
        Ok(())
257
6564
    }
258

            
259
3282
    extract(arti_path, path, '/', path_parsers, &mut keys)?;
260
3282
    extract(
261
3282
        arti_path,
262
3282
        Some(leaf),
263
3282
        DENOTATOR_SEP,
264
3282
        leaf_parsers,
265
3282
        &mut keys,
266
3282
    )?;
267
3282
    Ok(())
268
3282
}
269

            
270
/// Build a `KeyPathInfo` given the information about a key specifier
271
///
272
/// Calling pattern, to minimise macro-generated machine code,
273
/// is similar `arti_path_from_components`.
274
///
275
/// The macro-generated code parses the path into its KeySpecifier impl
276
/// (as an owned value) and then feeds references to the various fields
277
/// to `describe_via_components`.
278
2
pub fn describe_via_components(
279
2
    summary: &&str,
280
2
    role: &dyn RawKeySpecifierComponent,
281
2
    extra_keys: &&[&str],
282
2
    extra_info: &[&dyn KeySpecifierComponent],
283
2
) -> Result<KeyPathInfo, KeyPathError> {
284
2
    let mut info = KeyPathInfoBuilder::default();
285
2
    info.summary(summary.to_string());
286
2
    info.role({
287
2
        let mut s = String::new();
288
2
        role.append_to(&mut s)?;
289
2
        s
290
    });
291
10
    for (key, value) in izip!(*extra_keys, extra_info) {
292
10
        let value = KeySpecifierComponentPrettyHelper(*value).to_string();
293
10
        info.extra_info(*key, value);
294
10
    }
295
2
    Ok(info
296
2
        .build()
297
2
        .map_err(into_internal!("failed to build KeyPathInfo"))?)
298
2
}
299

            
300
define_derive_deftly! {
301
    /// A helper for implementing [`KeySpecifier`]s.
302
    ///
303
    /// Applies to a struct that has some static components (`prefix`, `role`),
304
    /// and a number of variable components represented by its fields.
305
    ///
306
    /// Implements `KeySpecifier` etc.
307
    ///
308
    /// Each field is either a path field (which becomes a component in the `ArtiPath`),
309
    /// or a denotator (which becomes *part* of the final component in the `ArtiPath`).
310
    ///
311
    /// The `prefix` is the first component of the [`ArtiPath`] of the [`KeySpecifier`].
312
    ///
313
    /// The role should be the name of the key in the Tor Specifications.
314
    /// The **lowercased** `role` is used as the _prefix of the last component_
315
    /// of the [`ArtiPath`] of the specifier.
316
    /// The `role` is followed by the denotators of the key.
317
    ///
318
    /// The denotator fields, if there are any,
319
    /// should be annotated with `#[denotator]`.
320
    ///
321
    /// The declaration order of the fields is important.
322
    /// The inner components of the [`ArtiPath`] of the specifier are built
323
    /// from the string representation of its path fields, taken in declaration order,
324
    /// followed by the encoding of its denotators, also taken in the order they were declared.
325
    /// As such, all path fields, must implement [`KeySpecifierComponent`].
326
    /// and all denotators must implement [`KeySpecifierComponent`].
327
    /// The denotators are separated from the rest of the path, and from each other,
328
    /// by `+` characters.
329
    ///
330
    /// For example, a key specifier with `prefix` `"foo"` and `role` `"bar"`
331
    /// will have an [`ArtiPath`] of the form
332
    /// `"foo/<field1_str>/<field2_str>/../bar[+<denotators>]"`.
333
    ///
334
    /// A key specifier of this form, with denotators that encode to "d1" and "d2",
335
    /// would look like this: `"foo/<field1_str>/<field2_str>/../bar+d1+d2"`.
336
    ///
337
    /// ### Results of applying this macro
338
    ///
339
    /// `#[derive(Deftly)] #[derive_deftly(KeySpecifier)] struct SomeKeySpec ...`
340
    /// generates:
341
    ///
342
    ///  * `impl `[`KeySpecifier`]` for SomeKeySpec`
343
    ///  * `struct SomeKeySpecPattern`,
344
    ///    a derived struct which contains an `Option` for each field.
345
    ///    `None` in the pattern means "any".
346
    ///  * `impl `[`KeySpecifierPattern`]` for SomeKeySpecPattern`
347
    ///  * `impl TryFrom<`[`KeyPath`]> for SomeKeySpec`
348
    ///  * Registration of an impl of [`KeyPathInfoExtractor`]
349
    ///    (on a private unit struct `SomeKeySpecInfoExtractor`)
350
    ///
351
    /// ### Custom attributes
352
    ///
353
    ///  * **`#[deftly(prefix)]`** (toplevel):
354
    ///    Specifies the fixed prefix (the first path component).
355
    ///    Must be a literal string.
356
    ///
357
    ///  * **`#[deftly(role = "...")]`** (toplevel):
358
    ///    Specifies the role - the initial portion of the leafname.
359
    ///    This should be the name of the key in the Tor Specifications.
360
    ///    Must be a literal string.
361
    ///    This or the field-level `#[deftly(role)]` must be specified.
362
    ///
363
    ///  * **`[adhoc(role)]` (field):
364
    ///    Specifies that the role is determined at runtime.
365
    ///    The field type must implement [`KeyDenotator`].
366
    ///
367
    ///  * **`#[deftly(summary = "...")]`** (summary, mandatory):
368
    ///    Specifies the summary; ends up as the `summary` field in [`KeyPathInfo`].
369
    ///    (See [`KeyPathInfoBuilder::summary()`].)
370
    ///    Must be a literal string.
371
    ///
372
    ///  * **`#[deftly(denotator)]`** (field):
373
    ///    Designates a field that should be represented
374
    ///    in the key file leafname, after the role.
375
    ///
376
    ///  * **`#[deftly(ctor_path = "expression")]`** (toplevel):
377
    ///    Specifies that this kind of key has a representation in C Tor keystores,
378
    ///    and provides an expression for computing the path.
379
    ///    The expression should have type `impl Fn(&Self) -> CTorPath`.
380
    ///
381
    ///    If not specified, the generated [`KeySpecifier::ctor_path`]
382
    ///    implementation will always return `None`.
383
    ///
384
    ///  * **`#[deftly(fixed_path_component = "component")]`** (field):
385
    ///    Before this field insert a fixed path component `component`.
386
    ///    (Can be even used before a denotator component,
387
    ///    to add a final fixed path component.)
388
    ///
389
    ///  * **`#[deftly(key_specifier = "type")]`** (field):
390
    ///    If this is the specifier for a public key, the specifier for
391
    ///    the corresponding keypair type.
392
    ///
393
    ///    If not specified, the generated [`KeySpecifier::keypair_specifier`]
394
    ///    implementation will always return `None`.
395
    //
396
    //     NOTE: The `KeySpecifier::keypair_specifier` implementation
397
    //     of the `ArtiPath` of a public key will always return `None`,
398
    //     even if the public key specifier it represents has a keypair specifier.
399
    //
400
    ///
401
    export KeySpecifier for struct:
402

            
403
    // A condition that evaluates to `true` for path fields.
404
    ${defcond F_IS_PATH not(any(fmeta(denotator), fmeta(role)))}
405
    ${defcond F_IS_ROLE all(fmeta(role), not(tmeta(role)))}
406

            
407
    #[doc = concat!("Pattern matching some or all [`", stringify!($tname), "`]")]
408
    #[allow(dead_code)] // Not everyone will need the pattern feature
409
    #[non_exhaustive]
410
    $tvis struct $<$tname Pattern><$tdefgens>
411
    where $twheres
412
    ${vdefbody $vname $(
413
        ${fattrs doc}
414
        ///
415
        /// `None` to match keys with any value for this field.
416
        $fvis $fname: Option<$ftype>,
417
    ) }
418

            
419
    // ** MAIN KNOWLEDGE OF HOW THE PATH IS CONSTRUCTED **
420
    //
421
    // These two user-defined expansions,
422
    //   $ARTI_PATH_COMPONENTS
423
    //   $ARTI_LEAF_COMPONENTS
424
    // expand to code for handling each path and leaf component,
425
    // in the order in which they appear in the ArtiPath.
426
    //
427
    // The "code for handling", by default, is:
428
    //   - for a field, take a reference to the field in `self`
429
    //   - for a fixed component, take a reference to a &'static str
430
    // in each case with a comma appended.
431
    // So this is suitable for including in a &[&dyn ...].
432
    //
433
    // The call site can override the behaviour by locally redefining,
434
    // the two user-defined expansions DO_FIELD and DO_LITERAL.
435
    //
436
    // DO_FIELD should expand to the code necessary to handle a field.
437
    // It probably wants to refer to $fname.
438
    //
439
    // DO_LITERAL should expand to the code necessary to handle a literal value.
440
    // When DO_LITERAL is called the user-defined expansion LIT will expand to
441
    // something like `${fmeta(...) as str}`, which will in turn expand to
442
    // a string literal.
443
    //
444
    // For use sites which want to distinguish the role from other fields:
445
    // DO_ROLE_FIELD and DO_ROLE_LITERAL are used for the role.
446
    // They default to expanding $DO_FIELD and $DO_LITERAL respectively.
447
    //
448
    // This is the *only* place that knows how ArtiPaths are constructed,
449
    // when the path syntax is defined using the KeySpecifier d-a macro.
450
    //
451
    // The actual code here is necessarily rather abstract.
452
    ${define ARTI_PATH_COMPONENTS {
453
        // #[deftly(prefix = ...)]
454
        ${define LIT ${tmeta(prefix) as str}}
455
        $DO_LITERAL
456

            
457
        ${for fields {
458
            // #[deftly(fixed_path_component = ...)]
459
            ${if fmeta(fixed_path_component) {
460
                // IWVNI d-a allowed arguments to use-defined expansions, but this will do
461
                ${define LIT ${fmeta(fixed_path_component) as str}}
462
                $DO_LITERAL
463
            }}
464
            // Path fields
465
            ${if F_IS_PATH { $DO_FIELD }}
466
        }}
467
    }}
468
    ${define ARTI_LEAF_COMPONENTS {
469
        ${if tmeta(role) {
470
            // #[deftly(role = ...)] on the toplevel
471
            ${define LIT { stringify!(${snake_case ${tmeta(role)}}) }}
472
            $DO_ROLE_LITERAL
473
        }}
474
        ${for fields {
475
            // #[deftly(role)] on a field
476
            ${if F_IS_ROLE { $DO_ROLE_FIELD }}
477
        }}
478
        ${for fields {
479
            // #[deftly(denotator)]
480
            ${if fmeta(denotator) { $DO_FIELD }}
481
        }}
482
    }}
483

            
484
    ${define DO_FIELD { &self.$fname, }}
485
    ${define DO_LITERAL { &$LIT, }}
486
    ${define DO_ROLE_FIELD { $DO_FIELD }}
487
    ${define DO_ROLE_LITERAL { $DO_LITERAL }}
488

            
489
    impl<$tgens> $crate::KeySpecifier for $ttype
490
    where $twheres
491
    {
492
1314
        fn arti_path(
493
1314
            &self,
494
1314
        ) -> std::result::Result<$crate::ArtiPath, $crate::ArtiPathUnavailableError> {
495
            use $crate::key_specifier_derive::*;
496

            
497
            arti_path_from_components(
498
                &[ $ARTI_PATH_COMPONENTS ],
499
                &[ $ARTI_LEAF_COMPONENTS ],
500
            )
501
        }
502

            
503
4
        fn ctor_path(&self) -> Option<$crate::CTorPath> {
504
            ${if tmeta(ctor_path) {
505
                Some( ${tmeta(ctor_path) as token_stream} (self) )
506
            } else {
507
                None
508
            }}
509
        }
510

            
511
191
        fn keypair_specifier(&self) -> Option<Box<dyn KeySpecifier>> {
512
            ${if tmeta(keypair_specifier) {
513
                Some(Box::new(std::convert::Into::<
514
                    ${tmeta(keypair_specifier) as token_stream}
515
                >::into(self)))
516
            } else {
517
                None
518
            }}
519
        }
520
    }
521

            
522
    impl<$tgens> $crate::KeySpecifierPattern for $<$tname Pattern><$tdefgens>
523
    where $twheres
524
    {
525
32
        fn arti_pattern(
526
32
            &self,
527
32
        ) -> std::result::Result<$crate::KeyPathPattern, $crate::key_specifier_derive::Bug> {
528
            use $crate::key_specifier_derive::*;
529

            
530
            arti_pattern_from_components(
531
                &[ $ARTI_PATH_COMPONENTS ],
532
                &[ $ARTI_LEAF_COMPONENTS ],
533
            )
534
        }
535

            
536
212
        fn new_any() -> Self {
537
            $< $tname Pattern > {
538
                $( $fname: None, )
539
            }
540
        }
541
    }
542

            
543
    struct $< $tname InfoExtractor >;
544

            
545
    impl<$tgens> $crate::KeyPathInfoExtractor for $< $tname InfoExtractor >
546
    where $twheres
547
    {
548
2
        fn describe(
549
2
            &self,
550
2
            path: &$crate::KeyPath,
551
2
        ) -> std::result::Result<$crate::KeyPathInfo, $crate::KeyPathError> {
552
            use $crate::key_specifier_derive::*;
553

            
554
            // Parse this path
555
            #[allow(unused_variables)] // Unused if no fields
556
            let spec = $ttype::try_from(path)?;
557

            
558
            // none of this cares about non-role literals
559
            // all the others three be explicitly defined each time
560
            ${define DO_LITERAL {}}
561

            
562
            static NON_ROLE_FIELD_KEYS: &[&str] = &[
563
                ${define DO_FIELD { stringify!($fname), }}
564
                ${define DO_ROLE_FIELD {}}
565
                ${define DO_ROLE_LITERAL {}}
566
                $ARTI_PATH_COMPONENTS
567
                $ARTI_LEAF_COMPONENTS
568
            ];
569

            
570
            describe_via_components(
571
                &${tmeta(summary) as str},
572

            
573
                // role
574
                ${define DO_FIELD {}}
575
                ${define DO_ROLE_FIELD { &spec.$fname, }}
576
                ${define DO_ROLE_LITERAL { &$LIT, }}
577
                $ARTI_LEAF_COMPONENTS
578

            
579
                &NON_ROLE_FIELD_KEYS,
580

            
581
                &[
582
                    ${define DO_FIELD { &spec.$fname, }}
583
                    ${define DO_ROLE_FIELD {}}
584
                    ${define DO_ROLE_LITERAL {}}
585
                    $ARTI_PATH_COMPONENTS
586
                    $ARTI_LEAF_COMPONENTS
587
                ],
588
            )
589
        }
590
    }
591

            
592
    impl<$tgens> TryFrom<&$crate::KeyPath> for $tname
593
    where $twheres
594
    {
595
        type Error = $crate::KeyPathError;
596

            
597
210
        fn try_from(path: &$crate::KeyPath) -> std::result::Result<$tname, Self::Error> {
598
96
            use $crate::key_specifier_derive::*;
599
96

            
600
96
            static FIELD_KEYS: &[&str] = &[
601
96
                ${define DO_LITERAL {}}
602
96
                ${define DO_FIELD { stringify!($fname), }}
603
96
                $ARTI_PATH_COMPONENTS
604
96
                $ARTI_LEAF_COMPONENTS
605
96
            ];
606
96

            
607
96
            #[allow(unused_mut)] // not needed if there are no fields
608
96
            #[allow(unused_variables)] // not needed if there are no fields
609
96
            let mut builder =
610
96
                <$<$tname Pattern>::<$tgens> as $crate::KeySpecifierPattern>::new_any();
611
96

            
612
96
            ${define DO_FIELD { &mut builder.$fname, }}
613
96
            ${define DO_LITERAL { &mut $LIT, }}
614
96

            
615
96
            parse_key_path(
616
96
                path,
617
96
                &FIELD_KEYS,
618
96
                &mut [ $ARTI_PATH_COMPONENTS ],
619
96
                &mut [ $ARTI_LEAF_COMPONENTS ],
620
96
            )?;
621
96

            
622
96
            #[allow(unused_variables)] // not needed if there are no fields
623
96
            let handle_none = || internal!("bad RawKeySpecifierComponentParser impl");
624

            
625
            Ok($tname { $(
626
                $fname: builder.$fname.ok_or_else(handle_none)?,
627
            ) })
628
        }
629
    }
630

            
631
    // Register the info extractor with `KeyMgr`.
632
    $crate::inventory::submit!(&$< $tname InfoExtractor > as &dyn $crate::KeyPathInfoExtractor);
633
}