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::{EitherOrBoth, Itertools, izip};
16

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

            
20
pub use crate::KeyPathInfoBuilder;
21
pub use tor_error::{Bug, internal, into_internal};
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
4371
    fn append_to(&self, s: &mut String) -> Result<(), Bug> {
39
4371
        self.to_slug()?.as_str().append_to(s)
40
4371
    }
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
132874
    fn append_to(&self, s: &mut String) -> Result<(), Bug> {
53
132874
        s.push_str(self);
54
132874
        Ok(())
55
132874
    }
56
}
57

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

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

            
79
35906
    Ok(path)
80
35906
}
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
35274
pub fn arti_path_from_components(
97
35274
    path_comps: &[&dyn RawKeySpecifierComponent],
98
35274
    leaf_comps: &[&dyn RawKeySpecifierComponent],
99
35274
) -> Result<ArtiPath, ArtiPathUnavailableError> {
100
35274
    Ok(arti_path_string_from_components(path_comps, leaf_comps)?
101
35274
        .try_into()
102
35274
        .map_err(into_internal!("bad ArtiPath from good components"))?)
103
35274
}
104

            
105
/// Make a `KeyPathPattern::Arti` like `pc/pc/pc/lc_lc_lc`
106
632
pub fn arti_pattern_from_components(
107
632
    path_comps: &[&dyn RawKeySpecifierComponent],
108
632
    leaf_comps: &[&dyn RawKeySpecifierComponent],
109
632
) -> Result<KeyPathPattern, Bug> {
110
632
    Ok(KeyPathPattern::Arti(arti_path_string_from_components(
111
632
        path_comps, leaf_comps,
112
    )?))
113
632
}
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_arti_path`].
123
    ParsedField,
124
    /// This was a literal, and it matched
125
    MatchedLiteral,
126
    /// Becomes [`ArtiPathError::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_arti_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
1782
    fn parse(&mut self, comp: &Slug) -> RawComponentParseResult {
148
1782
        let v = match T::from_slug(comp) {
149
1782
            Ok(v) => v,
150
            Err(e) => return RCPR::Invalid(e),
151
        };
152
1782
        *self = Some(v);
153
1782
        RCPR::ParsedField
154
1782
    }
155
}
156
impl<'s> RawKeySpecifierComponentParser for &'s str {
157
19276
    fn parse(&mut self, comp: &Slug) -> RawComponentParseResult {
158
19276
        if comp.as_str() == *self {
159
12204
            RCPR::MatchedLiteral
160
        } else {
161
7072
            RCPR::PatternNotMatched
162
        }
163
19276
    }
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
12966
pub fn parse_arti_path(
193
12966
    arti_path: &ArtiPath,
194
12966
    keys: &&[&str],
195
12966
    path_parsers: &mut Parsers,
196
12966
    leaf_parsers: &mut Parsers,
197
12966
) -> Result<(), ArtiPathError> {
198
12966
    let path = arti_path.as_str();
199

            
200
12966
    let (path, leaf) = match path.rsplit_once('/') {
201
12966
        Some((path, leaf)) => (Some(path), leaf),
202
        None => (None, path),
203
    };
204

            
205
12966
    let mut keys: &[&str] = keys;
206

            
207
    /// Split a string into components and parse each one
208
19276
    fn extract(
209
19276
        input: Option<&str>,
210
19276
        delim: char,
211
19276
        parsers: &mut Parsers,
212
19276
        keys: &mut &[&str],
213
19276
    ) -> Result<(), ArtiPathError> {
214
35600
        for ent in Itertools::zip_longest(
215
19664
            input.map(|input| input.split(delim)).into_iter().flatten(),
216
19276
            parsers,
217
        ) {
218
35600
            let EitherOrBoth::Both(comp, parser) = ent else {
219
                // wrong number of components
220
208
                return Err(ArtiPathError::PatternNotMatched);
221
            };
222

            
223
            // TODO would be nice to avoid allocating again here,
224
            // but I think that needs an `SlugRef`.
225
35392
            let comp = Slug::new(comp.to_owned())
226
35392
                .map_err(ArtiPathSyntaxError::Slug)
227
35392
                .map_err(ArtiPathError::InvalidArtiPath)?;
228

            
229
35392
            let missing_keys = || internal!("keys list too short, bad args to parse_arti_path");
230

            
231
35392
            match parser.parse(&comp) {
232
7072
                RCPR::PatternNotMatched => Err(ArtiPathError::PatternNotMatched),
233
                RCPR::Invalid(error) => Err(ArtiPathError::InvalidKeyPathComponentValue {
234
                    error,
235
                    key: keys.first().ok_or_else(missing_keys)?.to_string(),
236
                    value: comp,
237
                }),
238
                RCPR::ParsedField => {
239
16116
                    *keys = keys.split_first().ok_or_else(missing_keys)?.1;
240
16116
                    Ok(())
241
                }
242
12204
                RCPR::MatchedLiteral => Ok(()),
243
7072
            }?;
244
        }
245
11996
        Ok(())
246
19276
    }
247

            
248
12966
    extract(path, '/', path_parsers, &mut keys)?;
249
6310
    extract(Some(leaf), DENOTATOR_SEP, leaf_parsers, &mut keys)?;
250
5686
    Ok(())
251
12966
}
252

            
253
/// Build a `KeyPathInfo` given the information about a key specifier
254
///
255
/// Calling pattern, to minimise macro-generated machine code,
256
/// is similar `arti_path_from_components`.
257
///
258
/// The macro-generated code parses the path into its KeySpecifier impl
259
/// (as an owned value) and then feeds references to the various fields
260
/// to `describe_via_components`.
261
938
pub fn describe_via_components(
262
938
    summary: &&str,
263
938
    role: &dyn RawKeySpecifierComponent,
264
938
    extra_keys: &&[&str],
265
938
    extra_info: &[&dyn KeySpecifierComponent],
266
938
) -> Result<KeyPathInfo, Bug> {
267
938
    let mut info = KeyPathInfoBuilder::default();
268
938
    info.summary(summary.to_string());
269
938
    info.role({
270
938
        let mut s = String::new();
271
938
        role.append_to(&mut s)?;
272
938
        s
273
    });
274
1154
    for (key, value) in izip!(*extra_keys, extra_info) {
275
1154
        let value = KeySpecifierComponentPrettyHelper(*value).to_string();
276
1154
        info.extra_info(*key, value);
277
1154
    }
278
938
    info.build()
279
938
        .map_err(into_internal!("failed to build KeyPathInfo"))
280
938
}
281

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

            
399
    // A condition that evaluates to `true` for path fields.
400
    ${defcond F_IS_PATH not(any(fmeta(denotator), fmeta(role)))}
401
    ${defcond F_IS_ROLE all(fmeta(role), not(tmeta(role)))}
402

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

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

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

            
480
    ${define DO_FIELD { &self.$fname, }}
481
    ${define DO_LITERAL { &$LIT, }}
482
    ${define DO_ROLE_FIELD { $DO_FIELD }}
483
    ${define DO_ROLE_LITERAL { $DO_LITERAL }}
484

            
485
    impl<$tgens> $crate::KeySpecifier for $ttype
486
    where $twheres
487
    {
488
3427
        fn arti_path(
489
3427
            &self,
490
3427
        ) -> std::result::Result<$crate::ArtiPath, $crate::ArtiPathUnavailableError> {
491
            use $crate::key_specifier_derive::*;
492

            
493
            arti_path_from_components(
494
                &[ $ARTI_PATH_COMPONENTS ],
495
                &[ $ARTI_LEAF_COMPONENTS ],
496
            )
497
        }
498

            
499
298
        fn ctor_path(&self) -> Option<$crate::CTorPath> {
500
            <Self as $crate::CTorKeySpecifier>::ctor_path(self)
501
        }
502

            
503
576
        fn keypair_specifier(&self) -> Option<Box<dyn KeySpecifier>> {
504
            ${if tmeta(keypair_specifier) {
505
                Some(Box::new(std::convert::Into::<
506
                    ${tmeta(keypair_specifier) as token_stream}
507
                >::into(self)))
508
            } else {
509
                None
510
            }}
511
        }
512
    }
513

            
514
    impl<$tgens> $crate::KeySpecifierPattern for $<$tname Pattern><$tdefgens>
515
    where $twheres
516
    {
517
32
        fn arti_pattern(
518
32
            &self,
519
32
        ) -> std::result::Result<$crate::KeyPathPattern, $crate::key_specifier_derive::Bug> {
520
            use $crate::key_specifier_derive::*;
521

            
522
            arti_pattern_from_components(
523
                &[ $ARTI_PATH_COMPONENTS ],
524
                &[ $ARTI_LEAF_COMPONENTS ],
525
            )
526
        }
527

            
528
10121
        fn new_any() -> Self {
529
            $< $tname Pattern > {
530
                $( $fname: None, )
531
            }
532
        }
533
    }
534

            
535
    struct $< $tname InfoExtractor >;
536

            
537
    impl<$tgens> $crate::KeyPathInfoExtractor for $< $tname InfoExtractor >
538
    where $twheres
539
    {
540
9909
        fn describe(
541
9909
            &self,
542
9909
            path: &$crate::KeyPath,
543
9909
        ) -> std::result::Result<$crate::KeyPathInfo, $crate::KeyPathError> {
544
            use $crate::key_specifier_derive::*;
545

            
546
            // Parse this path
547
            #[allow(unused_variables)] // Unused if no fields
548
            let spec = $ttype::try_from(path)?;
549

            
550
            // none of this cares about non-role literals
551
            // all the others three be explicitly defined each time
552
            ${define DO_LITERAL {}}
553

            
554
            static NON_ROLE_FIELD_KEYS: &[&str] = &[
555
                ${define DO_FIELD { stringify!($fname), }}
556
                ${define DO_ROLE_FIELD {}}
557
                ${define DO_ROLE_LITERAL {}}
558
                $ARTI_PATH_COMPONENTS
559
                $ARTI_LEAF_COMPONENTS
560
            ];
561

            
562
            describe_via_components(
563
                &${tmeta(summary) as str},
564

            
565
                // role
566
                ${define DO_FIELD {}}
567
                ${define DO_ROLE_FIELD { &spec.$fname, }}
568
                ${define DO_ROLE_LITERAL { &$LIT, }}
569
                $ARTI_LEAF_COMPONENTS
570

            
571
                &NON_ROLE_FIELD_KEYS,
572

            
573
                &[
574
                    ${define DO_FIELD { &spec.$fname, }}
575
                    ${define DO_ROLE_FIELD {}}
576
                    ${define DO_ROLE_LITERAL {}}
577
                    $ARTI_PATH_COMPONENTS
578
                    $ARTI_LEAF_COMPONENTS
579
                ],
580
            ).map_err($crate::KeyPathError::Bug)
581
        }
582
    }
583

            
584
    impl<$tgens> TryFrom<&$crate::KeyPath> for $tname
585
    where $twheres
586
    {
587
        type Error = $crate::KeyPathError;
588

            
589
10119
        fn try_from(path: &$crate::KeyPath) -> std::result::Result<$tname, Self::Error> {
590
            use $crate::key_specifier_derive::*;
591

            
592
            static FIELD_KEYS: &[&str] = &[
593
                ${define DO_LITERAL {}}
594
                ${define DO_FIELD { stringify!($fname), }}
595
                $ARTI_PATH_COMPONENTS
596
                $ARTI_LEAF_COMPONENTS
597
            ];
598

            
599
            #[allow(unused_mut)] // not needed if there are no fields
600
            #[allow(unused_variables)] // not needed if there are no fields
601
            let mut builder =
602
                <$<$tname Pattern>::<$tgens> as $crate::KeySpecifierPattern>::new_any();
603

            
604
            ${define DO_FIELD { &mut builder.$fname, }}
605
            ${define DO_LITERAL { &mut $LIT, }}
606

            
607
            #[allow(unused_variables)] // CTorPath is only used with ctor_path(..)
608
            match path {
609
                $crate::KeyPath::Arti(path) => {
610
                    parse_arti_path(
611
                        path,
612
                        &FIELD_KEYS,
613
                        &mut [ $ARTI_PATH_COMPONENTS ],
614
                        &mut [ $ARTI_LEAF_COMPONENTS ],
615
                    ).map_err(|err| $crate::KeyPathError::Arti { path: path.clone(), err })?;
616
                },
617
                $crate::KeyPath::CTor(path) => {
618
                    return <Self as $crate::CTorKeySpecifier>::from_ctor_path(path.clone())
619
                        .map_err(|err| $crate::KeyPathError::CTor { path: path.clone(), err });
620
                },
621
                #[allow(unreachable_patterns)] // This is reachable if used outside of tor-keymgr
622
                &_ => {
623
                    return Err(internal!("unrecognized key path?!").into());
624
                }
625
            };
626

            
627
            #[allow(unused_variables)] // not needed if there are no fields
628
            let handle_none = || internal!("bad RawKeySpecifierComponentParser impl");
629

            
630
            Ok($tname { $(
631
                $fname: builder.$fname.ok_or_else(handle_none)?,
632
            ) })
633
        }
634
    }
635

            
636
    ${if tmeta(ctor_path) {
637

            
638
    ${define CTOR_PATH_VARIANT ${tmeta(ctor_path) as path}}
639

            
640
    impl<$tgens> $crate::CTorKeySpecifier for $ttype
641
    where $twheres
642
    {
643
298
        fn ctor_path(&self) -> Option<$crate::CTorPath> {
644
            Some($crate::CTorPath :: $CTOR_PATH_VARIANT {
645
                $( $fname: self.$fname.clone(), )
646
            })
647
        }
648

            
649
339
        fn from_ctor_path(
650
339
            path: $crate::CTorPath
651
339
        ) -> std::result::Result<Self, $crate::CTorPathError> {
652

            
653
            match path {
654
                $crate::CTorPath :: $CTOR_PATH_VARIANT { $( $fname, )} => {
655
                    Ok( Self { $( $fname, ) })
656
                },
657
                _ => Err($crate::CTorPathError::KeySpecifierMismatch(stringify!($tname).into())),
658
            }
659
        }
660
    }
661

            
662
    } else {
663
    impl<$tgens> $crate::CTorKeySpecifier for $ttype
664
    where $twheres
665
    {
666
        fn ctor_path(&self) -> Option<$crate::CTorPath> {
667
            None
668
        }
669

            
670
2588
        fn from_ctor_path(
671
2588
            _: $crate::CTorPath
672
2588
        ) -> std::result::Result<Self, $crate::CTorPathError> {
673
            Err($crate::CTorPathError::MissingCTorPath(stringify!($tname).to_string()))
674
        }
675
    }
676

            
677
    }}
678

            
679
    // #[deftly(has_certificate = ...)] on the toplevel
680
    ${defcond HAS_CERT
681
        any(tmeta(has_certificate(certificate)),
682
            tmeta(has_certificate(signed_with)))
683
    }
684

            
685
    // Whether *all* the required tmeta fields were specified at the top level
686
    ${defcond HAS_ALL_CERT_FIELDS
687
        all(tmeta(has_certificate(certificate)),
688
            tmeta(has_certificate(signed_with)))
689
    }
690

            
691
    ${if HAS_CERT {
692
        ${if HAS_ALL_CERT_FIELDS {
693

            
694
            ${define SIGNING_KEY_SPEC ${tmeta(has_certificate(signed_with)) as ident}}
695
            ${define CERT_SPEC ${tmeta(has_certificate(certificate)) as ident}}
696

            
697
            #[doc = concat!("The certificate specifier of a [`", stringify!($tname), "`]")]
698
            #[non_exhaustive]
699
            $tvis struct $CERT_SPEC<$tdefgens>
700
            where $twheres
701
            ${vdefbody $vname $(
702
                /// The specifier of the signing key.
703
                ///
704
                // TODO: in practice this is currently non-optional. However, the reason I made it
705
                // an `Option` is because later down the line we might want to support certificates
706
                // signed with keys that do not have a key specifier at all.
707
                //
708
                // See https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/2565#note_3126535
709
                $fvis signed_with: Option<$SIGNING_KEY_SPEC>,
710
                /// The specifier of the subject key.
711
                $fvis subject: $ttype,
712

            
713
                // TODO: support certificate denotators?
714
            ) }
715

            
716
            impl<$tgens> $crate::KeyCertificateSpecifier for $CERT_SPEC<$tdefgens>
717
            where $twheres
718
            {
719
                fn cert_denotators(&self) -> Vec<&dyn $crate::KeySpecifierComponent> {
720
                    // TODO: support certificate denotators?
721
                    vec![]
722
                }
723

            
724
2
                fn signing_key_specifier(&self) -> Option<&dyn $crate::KeySpecifier> {
725
2
                    self.signed_with.as_ref().map(|key| key as &dyn $crate::KeySpecifier)
726
                }
727

            
728
2
                fn subject_key_specifier(&self) -> &dyn $crate::KeySpecifier {
729
                    &self.subject
730
                }
731
            }
732
        } else {
733
            // TODO: we might want to make signed_with optional later down the line
734
            // (but when we do, we also should change all the certificate APIs to
735
            // take an optional signing key argument)
736
            //
737
            // See https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/2565#note_3126535
738
            ${error "has_certificate requires both \"certificate\" and \"signed_with\""}
739
        }}
740
    }}
741

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